dcicutils 8.4.0.1b10__py3-none-any.whl → 8.4.0.1b12__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dcicutils might be problematic. Click here for more details.
- dcicutils/portal_utils.py +267 -0
- dcicutils/structured_data.py +11 -215
- {dcicutils-8.4.0.1b10.dist-info → dcicutils-8.4.0.1b12.dist-info}/METADATA +1 -1
- {dcicutils-8.4.0.1b10.dist-info → dcicutils-8.4.0.1b12.dist-info}/RECORD +7 -6
- {dcicutils-8.4.0.1b10.dist-info → dcicutils-8.4.0.1b12.dist-info}/LICENSE.txt +0 -0
- {dcicutils-8.4.0.1b10.dist-info → dcicutils-8.4.0.1b12.dist-info}/WHEEL +0 -0
- {dcicutils-8.4.0.1b10.dist-info → dcicutils-8.4.0.1b12.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,267 @@
|
|
1
|
+
from collections import deque
|
2
|
+
from pyramid.paster import get_app
|
3
|
+
from pyramid.router import Router
|
4
|
+
import re
|
5
|
+
import requests
|
6
|
+
from requests.models import Response as RequestResponse
|
7
|
+
from typing import Optional, Type, Union
|
8
|
+
from webtest.app import TestApp, TestResponse
|
9
|
+
from dcicutils.common import OrchestratedApp, APP_CGAP, APP_FOURFRONT, APP_SMAHT, ORCHESTRATED_APPS
|
10
|
+
from dcicutils.creds_utils import CGAPKeyManager, FourfrontKeyManager, SMaHTKeyManager
|
11
|
+
from dcicutils.ff_utils import get_metadata, get_schema, patch_metadata, post_metadata
|
12
|
+
from dcicutils.misc_utils import to_camel_case, VirtualApp
|
13
|
+
from dcicutils.zip_utils import temporary_file
|
14
|
+
|
15
|
+
Portal = Type["Portal"] # Forward type reference for type hints.
|
16
|
+
FILE_SCHEMA_NAME = "File"
|
17
|
+
|
18
|
+
|
19
|
+
class Portal:
|
20
|
+
|
21
|
+
def __init__(self,
|
22
|
+
arg: Optional[Union[VirtualApp, TestApp, Router, Portal, dict, tuple, str]] = None,
|
23
|
+
env: Optional[str] = None, app: Optional[OrchestratedApp] = None, server: Optional[str] = None,
|
24
|
+
key: Optional[Union[dict, tuple]] = None,
|
25
|
+
vapp: Optional[Union[VirtualApp, TestApp, Router, Portal, str]] = None,
|
26
|
+
portal: Optional[Union[VirtualApp, TestApp, Router, Portal, str]] = None) -> Portal:
|
27
|
+
if vapp and not portal:
|
28
|
+
portal = vapp
|
29
|
+
if ((isinstance(arg, (VirtualApp, TestApp, Router, Portal)) or
|
30
|
+
isinstance(arg, str) and arg.endswith(".ini")) and not portal):
|
31
|
+
portal = arg
|
32
|
+
elif isinstance(arg, str) and not env:
|
33
|
+
env = arg
|
34
|
+
elif (isinstance(arg, dict) or isinstance(arg, tuple)) and not key:
|
35
|
+
key = arg
|
36
|
+
if not app and env:
|
37
|
+
if env.startswith(APP_SMAHT):
|
38
|
+
app = APP_SMAHT
|
39
|
+
elif env.startswith(APP_CGAP):
|
40
|
+
app = APP_CGAP
|
41
|
+
elif env.startswith(APP_FOURFRONT):
|
42
|
+
app = APP_FOURFRONT
|
43
|
+
if isinstance(portal, Portal):
|
44
|
+
self._vapp = portal._vapp
|
45
|
+
self._env = portal._env
|
46
|
+
self._app = portal._app
|
47
|
+
self._server = portal._server
|
48
|
+
self._key = portal._key
|
49
|
+
self._key_pair = portal._key_pair
|
50
|
+
self._key_file = portal._key_file
|
51
|
+
return
|
52
|
+
self._vapp = None
|
53
|
+
self._env = env
|
54
|
+
self._app = app
|
55
|
+
self._server = server
|
56
|
+
self._key = None
|
57
|
+
self._key_pair = None
|
58
|
+
self._key_file = None
|
59
|
+
if isinstance(portal, (VirtualApp, TestApp)):
|
60
|
+
self._vapp = portal
|
61
|
+
elif isinstance(portal, (Router, str)):
|
62
|
+
self._vapp = Portal._create_testapp(portal)
|
63
|
+
elif isinstance(key, dict):
|
64
|
+
self._key = key
|
65
|
+
self._key_pair = (key.get("key"), key.get("secret")) if key else None
|
66
|
+
if key_server := key.get("server"):
|
67
|
+
self._server = key_server
|
68
|
+
elif isinstance(key, tuple) and len(key) >= 2:
|
69
|
+
self._key = {"key": key[0], "secret": key[1]}
|
70
|
+
self._key_pair = key
|
71
|
+
elif isinstance(env, str):
|
72
|
+
key_managers = {APP_CGAP: CGAPKeyManager, APP_FOURFRONT: FourfrontKeyManager, APP_SMAHT: SMaHTKeyManager}
|
73
|
+
if not (key_manager := key_managers.get(self._app)) or not (key_manager := key_manager()):
|
74
|
+
raise Exception(f"Invalid app name: {self._app} (valid: {', '.join(ORCHESTRATED_APPS)}).")
|
75
|
+
if isinstance(env, str):
|
76
|
+
self._key = key_manager.get_keydict_for_env(env)
|
77
|
+
if key_server := self._key.get("server"):
|
78
|
+
self._server = key_server
|
79
|
+
elif isinstance(self._server, str):
|
80
|
+
self._key = key_manager.get_keydict_for_server(self._server)
|
81
|
+
self._key_pair = key_manager.keydict_to_keypair(self._key) if self._key else None
|
82
|
+
self._key_file = key_manager.keys_file
|
83
|
+
|
84
|
+
@property
|
85
|
+
def env(self):
|
86
|
+
return self._env
|
87
|
+
|
88
|
+
@property
|
89
|
+
def app(self):
|
90
|
+
return self._app
|
91
|
+
|
92
|
+
@property
|
93
|
+
def server(self):
|
94
|
+
return self._server
|
95
|
+
|
96
|
+
@property
|
97
|
+
def key(self):
|
98
|
+
return self._key
|
99
|
+
|
100
|
+
@property
|
101
|
+
def key_pair(self):
|
102
|
+
return self._key_pair
|
103
|
+
|
104
|
+
@property
|
105
|
+
def key_file(self):
|
106
|
+
return self._key_file
|
107
|
+
|
108
|
+
@property
|
109
|
+
def vapp(self):
|
110
|
+
return self._vapp
|
111
|
+
|
112
|
+
def get_metadata(self, object_id: str) -> Optional[dict]:
|
113
|
+
return get_metadata(obj_id=object_id, vapp=self._vapp, key=self._key)
|
114
|
+
|
115
|
+
def patch_metadata(self, object_id: str, data: str) -> Optional[dict]:
|
116
|
+
if self._key:
|
117
|
+
return patch_metadata(obj_id=object_id, patch_item=data, key=self._key)
|
118
|
+
return self.patch(f"/{object_id}", data)
|
119
|
+
|
120
|
+
def post_metadata(self, object_type: str, data: str) -> Optional[dict]:
|
121
|
+
if self._key:
|
122
|
+
return post_metadata(schema_name=object_type, post_item=data, key=self._key)
|
123
|
+
return self.post(f"/{object_type}", data)
|
124
|
+
|
125
|
+
def get(self, uri: str, follow: bool = True, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
126
|
+
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
127
|
+
response = self._vapp.get(self._uri(uri), **self._kwargs(**kwargs))
|
128
|
+
if response and response.status_code in [301, 302, 303, 307, 308] and follow:
|
129
|
+
response = response.follow()
|
130
|
+
return self._response(response)
|
131
|
+
return requests.get(self._uri(uri), allow_redirects=follow, **self._kwargs(**kwargs))
|
132
|
+
|
133
|
+
def patch(self, uri: str, data: Optional[dict] = None,
|
134
|
+
json: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
135
|
+
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
136
|
+
return self._vapp.patch_json(self._uri(uri), json or data, **self._kwargs(**kwargs))
|
137
|
+
return requests.patch(self._uri(uri), json=json or data, **self._kwargs(**kwargs))
|
138
|
+
|
139
|
+
def post(self, uri: str, data: Optional[dict] = None, json: Optional[dict] = None,
|
140
|
+
files: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
141
|
+
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
142
|
+
if files:
|
143
|
+
return self._vapp.post(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
144
|
+
else:
|
145
|
+
return self._vapp.post_json(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
146
|
+
return requests.post(self._uri(uri), json=json or data, files=files, **self._kwargs(**kwargs))
|
147
|
+
|
148
|
+
def get_schema(self, schema_name: str) -> Optional[dict]:
|
149
|
+
return get_schema(self.schema_name(schema_name), portal_vapp=self._vapp, key=self._key)
|
150
|
+
|
151
|
+
def get_schemas(self) -> dict:
|
152
|
+
return self.get("/profiles/").json()
|
153
|
+
|
154
|
+
@staticmethod
|
155
|
+
def schema_name(name: str) -> str:
|
156
|
+
return to_camel_case(name)
|
157
|
+
|
158
|
+
def is_file_schema(self, schema_name: str) -> bool:
|
159
|
+
if super_type_map := self.get_schemas_super_type_map():
|
160
|
+
if file_super_type := super_type_map.get(FILE_SCHEMA_NAME):
|
161
|
+
return self.schema_name(schema_name) in file_super_type
|
162
|
+
return False
|
163
|
+
|
164
|
+
def get_schemas_super_type_map(self) -> dict:
|
165
|
+
"""
|
166
|
+
Returns the "super type map" for all of the known schemas (via /profiles).
|
167
|
+
This is a dictionary of all types which have (one or more) sub-types whose value is
|
168
|
+
an array of all of those sub-types (direct and all descendents), in breadth first order.
|
169
|
+
"""
|
170
|
+
def breadth_first(super_type_map: dict, super_type_name: str) -> dict:
|
171
|
+
result = []
|
172
|
+
queue = deque(super_type_map.get(super_type_name, []))
|
173
|
+
while queue:
|
174
|
+
result.append(sub_type_name := queue.popleft())
|
175
|
+
if sub_type_name in super_type_map:
|
176
|
+
queue.extend(super_type_map[sub_type_name])
|
177
|
+
return result
|
178
|
+
if not (schemas := self.get_schemas()):
|
179
|
+
return {}
|
180
|
+
super_type_map = {}
|
181
|
+
for type_name in schemas:
|
182
|
+
if super_type_name := schemas[type_name].get("rdfs:subClassOf"):
|
183
|
+
super_type_name = super_type_name.replace("/profiles/", "").replace(".json", "")
|
184
|
+
if super_type_name != "Item":
|
185
|
+
if not super_type_map.get(super_type_name):
|
186
|
+
super_type_map[super_type_name] = [type_name]
|
187
|
+
elif type_name not in super_type_map[super_type_name]:
|
188
|
+
super_type_map[super_type_name].append(type_name)
|
189
|
+
super_type_map_flattened = {}
|
190
|
+
for super_type_name in super_type_map:
|
191
|
+
super_type_map_flattened[super_type_name] = breadth_first(super_type_map, super_type_name)
|
192
|
+
return super_type_map_flattened
|
193
|
+
|
194
|
+
def _uri(self, uri: str) -> str:
|
195
|
+
if not isinstance(uri, str) or not uri:
|
196
|
+
return "/"
|
197
|
+
if uri.lower().startswith("http://") or uri.lower().startswith("https://"):
|
198
|
+
return uri
|
199
|
+
uri = re.sub(r"/+", "/", uri)
|
200
|
+
return (self._server + ("/" if uri.startswith("/") else "") + uri) if self._server else uri
|
201
|
+
|
202
|
+
def _kwargs(self, **kwargs) -> dict:
|
203
|
+
result_kwargs = {"headers":
|
204
|
+
kwargs.get("headers", {"Content-type": "application/json", "Accept": "application/json"})}
|
205
|
+
if self._key_pair:
|
206
|
+
result_kwargs["auth"] = self._key_pair
|
207
|
+
if isinstance(timeout := kwargs.get("timeout"), int):
|
208
|
+
result_kwargs["timeout"] = timeout
|
209
|
+
return result_kwargs
|
210
|
+
|
211
|
+
def _response(self, response) -> Optional[RequestResponse]:
|
212
|
+
if response and isinstance(getattr(response.__class__, "json"), property):
|
213
|
+
class RequestResponseWrapper: # For consistency change json property to method.
|
214
|
+
def __init__(self, response, **kwargs):
|
215
|
+
super().__init__(**kwargs)
|
216
|
+
self._response = response
|
217
|
+
def __getattr__(self, attr): # noqa
|
218
|
+
return getattr(self._response, attr)
|
219
|
+
def json(self): # noqa
|
220
|
+
return self._response.json
|
221
|
+
response = RequestResponseWrapper(response)
|
222
|
+
return response
|
223
|
+
|
224
|
+
@staticmethod
|
225
|
+
def create_for_testing(ini_file: Optional[str] = None) -> Portal:
|
226
|
+
if isinstance(ini_file, str):
|
227
|
+
return Portal(Portal._create_testapp(ini_file))
|
228
|
+
minimal_ini_for_unit_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
|
229
|
+
with temporary_file(content=minimal_ini_for_unit_testing, suffix=".ini") as ini_file:
|
230
|
+
return Portal(Portal._create_testapp(ini_file))
|
231
|
+
|
232
|
+
@staticmethod
|
233
|
+
def create_for_testing_local(ini_file: Optional[str] = None) -> Portal:
|
234
|
+
if isinstance(ini_file, str) and ini_file:
|
235
|
+
return Portal(Portal._create_testapp(ini_file))
|
236
|
+
minimal_ini_for_testing_local = "\n".join([
|
237
|
+
"[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
|
238
|
+
"sqlalchemy.url = postgresql://postgres@localhost:5441/postgres?host=/tmp/snovault/pgdata",
|
239
|
+
"multiauth.groupfinder = encoded.authorization.smaht_groupfinder",
|
240
|
+
"multiauth.policies = auth0 session remoteuser accesskey",
|
241
|
+
"multiauth.policy.session.namespace = mailto",
|
242
|
+
"multiauth.policy.session.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
243
|
+
"multiauth.policy.session.base = pyramid.authentication.SessionAuthenticationPolicy",
|
244
|
+
"multiauth.policy.remoteuser.namespace = remoteuser",
|
245
|
+
"multiauth.policy.remoteuser.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
246
|
+
"multiauth.policy.remoteuser.base = pyramid.authentication.RemoteUserAuthenticationPolicy",
|
247
|
+
"multiauth.policy.accesskey.namespace = accesskey",
|
248
|
+
"multiauth.policy.accesskey.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
249
|
+
"multiauth.policy.accesskey.base = encoded.authentication.BasicAuthAuthenticationPolicy",
|
250
|
+
"multiauth.policy.accesskey.check = encoded.authentication.basic_auth_check",
|
251
|
+
"multiauth.policy.auth0.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
252
|
+
"multiauth.policy.auth0.namespace = auth0",
|
253
|
+
"multiauth.policy.auth0.base = encoded.authentication.Auth0AuthenticationPolicy"
|
254
|
+
])
|
255
|
+
with temporary_file(content=minimal_ini_for_testing_local, suffix=".ini") as minimal_ini_file:
|
256
|
+
return Portal(Portal._create_testapp(minimal_ini_file))
|
257
|
+
|
258
|
+
@staticmethod
|
259
|
+
def _create_testapp(value: Union[str, Router, TestApp] = "development.ini") -> TestApp:
|
260
|
+
"""
|
261
|
+
Creates and returns a TestApp. Refactored out of above loadxl code to consolidate at a
|
262
|
+
single point; also for use by the generate_local_access_key and view_local_object scripts.
|
263
|
+
"""
|
264
|
+
if isinstance(value, TestApp):
|
265
|
+
return value
|
266
|
+
app = value if isinstance(value, Router) else get_app(value, "app")
|
267
|
+
return TestApp(app, {"HTTP_ACCEPT": "application/json", "REMOTE_USER": "TEST"})
|
dcicutils/structured_data.py
CHANGED
@@ -1,24 +1,19 @@
|
|
1
|
-
from collections import deque
|
2
1
|
import copy
|
3
2
|
from functools import lru_cache
|
4
3
|
import json
|
5
4
|
from jsonschema import Draft7Validator as SchemaValidator
|
6
5
|
import os
|
7
|
-
from pyramid.paster import get_app
|
8
6
|
from pyramid.router import Router
|
9
7
|
import re
|
10
|
-
import requests
|
11
|
-
from requests.models import Response as RequestResponse
|
12
8
|
import sys
|
13
9
|
from typing import Any, Callable, List, Optional, Tuple, Type, Union
|
14
|
-
from webtest.app import TestApp
|
15
|
-
from dcicutils.common import OrchestratedApp
|
16
|
-
from dcicutils.creds_utils import CGAPKeyManager, FourfrontKeyManager, SMaHTKeyManager
|
10
|
+
from webtest.app import TestApp
|
11
|
+
from dcicutils.common import OrchestratedApp
|
17
12
|
from dcicutils.data_readers import CsvReader, Excel, RowReader
|
18
|
-
from dcicutils.ff_utils import get_metadata, get_schema, patch_metadata, post_metadata
|
19
13
|
from dcicutils.misc_utils import (create_object, load_json_if, merge_objects, remove_empty_properties, right_trim,
|
20
|
-
split_string, to_boolean,
|
21
|
-
from dcicutils.
|
14
|
+
split_string, to_boolean, to_enum, to_float, to_integer, VirtualApp)
|
15
|
+
from dcicutils.portal_utils import Portal as PortalBase
|
16
|
+
from dcicutils.zip_utils import unpack_gz_file_to_temporary_file, unpack_files
|
22
17
|
|
23
18
|
|
24
19
|
# Classes/functions to parse a CSV or Excel Spreadsheet into structured data, using a specialized
|
@@ -36,12 +31,10 @@ ARRAY_VALUE_DELIMITER_ESCAPE_CHAR = "\\"
|
|
36
31
|
ARRAY_NAME_SUFFIX_CHAR = "#"
|
37
32
|
ARRAY_NAME_SUFFIX_REGEX = re.compile(rf"{ARRAY_NAME_SUFFIX_CHAR}\d+")
|
38
33
|
DOTTED_NAME_DELIMITER_CHAR = "."
|
39
|
-
FILE_SCHEMA_NAME = "File"
|
40
34
|
FILE_SCHEMA_NAME_PROPERTY = "filename"
|
41
35
|
|
42
36
|
# Forward type references for type hints.
|
43
37
|
Portal = Type["Portal"]
|
44
|
-
PortalBase = Type["PortalBase"]
|
45
38
|
Schema = Type["Schema"]
|
46
39
|
StructuredDataSet = Type["StructuredDataSet"]
|
47
40
|
|
@@ -526,7 +519,7 @@ class Schema:
|
|
526
519
|
@staticmethod
|
527
520
|
def type_name(value: str) -> str: # File or other name.
|
528
521
|
name = os.path.basename(value).replace(" ", "") if isinstance(value, str) else ""
|
529
|
-
return
|
522
|
+
return PortalBase.schema_name(name[0:dot] if (dot := name.rfind(".")) > 0 else name)
|
530
523
|
|
531
524
|
@staticmethod
|
532
525
|
def array_indices(name: str) -> Tuple[Optional[str], Optional[List[int]]]:
|
@@ -540,179 +533,15 @@ class Schema:
|
|
540
533
|
return (name, indices) if indices else (None, None)
|
541
534
|
|
542
535
|
|
543
|
-
class PortalBase:
|
544
|
-
|
545
|
-
def __init__(self,
|
546
|
-
arg: Optional[Union[VirtualApp, TestApp, Router, Portal, dict, tuple, str]] = None,
|
547
|
-
env: Optional[str] = None, app: OrchestratedApp = APP_SMAHT, server: Optional[str] = None,
|
548
|
-
key: Optional[Union[dict, tuple]] = None,
|
549
|
-
portal: Optional[Union[VirtualApp, TestApp, Router, Portal, str]] = None) -> PortalBase:
|
550
|
-
if ((isinstance(arg, (VirtualApp, TestApp, Router, Portal)) or
|
551
|
-
isinstance(arg, str) and arg.endswith(".ini")) and not portal):
|
552
|
-
portal = arg
|
553
|
-
elif isinstance(arg, str) and not env:
|
554
|
-
env = arg
|
555
|
-
elif (isinstance(arg, dict) or isinstance(arg, tuple)) and not key:
|
556
|
-
key = arg
|
557
|
-
self._vapp = None
|
558
|
-
self._key = None
|
559
|
-
self._key_pair = None
|
560
|
-
self._server = None
|
561
|
-
if isinstance(portal, Portal):
|
562
|
-
self._vapp = portal._vapp
|
563
|
-
self._key = portal._key
|
564
|
-
self._key_pair = portal._key_pair
|
565
|
-
self._server = portal._server
|
566
|
-
elif isinstance(portal, (VirtualApp, TestApp)):
|
567
|
-
self._vapp = portal
|
568
|
-
elif isinstance(portal, (Router, str)):
|
569
|
-
self._vapp = PortalBase._create_testapp(portal)
|
570
|
-
elif isinstance(key, dict):
|
571
|
-
self._key = key
|
572
|
-
self._key_pair = (key.get("key"), key.get("secret")) if key else None
|
573
|
-
self._server = key.get("server")
|
574
|
-
elif isinstance(key, tuple) and len(key) >= 2:
|
575
|
-
self._key = {"key": key[0], "secret": key[1]}
|
576
|
-
self._key_pair = key
|
577
|
-
elif isinstance(env, str):
|
578
|
-
key_managers = {APP_CGAP: CGAPKeyManager, APP_FOURFRONT: FourfrontKeyManager, APP_SMAHT: SMaHTKeyManager}
|
579
|
-
if not (key_manager := key_managers.get(app)) or not (key_manager := key_manager()):
|
580
|
-
raise Exception(f"Invalid app name: {app} (valid: {', '.join(ORCHESTRATED_APPS)}).")
|
581
|
-
if isinstance(env, str):
|
582
|
-
self._key = key_manager.get_keydict_for_env(env)
|
583
|
-
self._server = self._key.get("server") if self._key else None
|
584
|
-
elif isinstance(server, str):
|
585
|
-
self._key = key_manager.get_keydict_for_server(server)
|
586
|
-
self._server = server
|
587
|
-
self._key_pair = key_manager.keydict_to_keypair(self._key) if self._key else None
|
588
|
-
|
589
|
-
def get_metadata(self, object_id: str) -> Optional[dict]:
|
590
|
-
return get_metadata(obj_id=object_id, vapp=self._vapp, key=self._key)
|
591
|
-
|
592
|
-
def patch_metadata(self, object_id: str, data: str) -> Optional[dict]:
|
593
|
-
if self._key:
|
594
|
-
return patch_metadata(obj_id=object_id, patch_item=data, key=self._key)
|
595
|
-
return self.patch(f"/{object_id}", data)
|
596
|
-
|
597
|
-
def post_metadata(self, object_type: str, data: str) -> Optional[dict]:
|
598
|
-
if self._key:
|
599
|
-
return post_metadata(schema_name=object_type, post_item=data, key=self._key)
|
600
|
-
return self.post(f"/{object_type}", data)
|
601
|
-
|
602
|
-
def get(self, uri: str, follow: bool = True, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
603
|
-
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
604
|
-
response = self._vapp.get(self._uri(uri), **self._kwargs(**kwargs))
|
605
|
-
if response and response.status_code in [301, 302, 303, 307, 308] and follow:
|
606
|
-
response = response.follow()
|
607
|
-
return self._response(response)
|
608
|
-
return requests.get(self._uri(uri), allow_redirects=follow, **self._kwargs(**kwargs))
|
609
|
-
|
610
|
-
def patch(self, uri: str, data: Optional[dict] = None,
|
611
|
-
json: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
612
|
-
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
613
|
-
return self._vapp.patch_json(self._uri(uri), json or data, **self._kwargs(**kwargs))
|
614
|
-
return requests.patch(self._uri(uri), json=json or data, **self._kwargs(**kwargs))
|
615
|
-
|
616
|
-
def post(self, uri: str, data: Optional[dict] = None, json: Optional[dict] = None,
|
617
|
-
files: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
618
|
-
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
619
|
-
if files:
|
620
|
-
return self._vapp.post(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
621
|
-
else:
|
622
|
-
return self._vapp.post_json(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
623
|
-
return requests.post(self._uri(uri), json=json or data, files=files, **self._kwargs(**kwargs))
|
624
|
-
|
625
|
-
def get_schema(self, schema_name: str) -> Optional[dict]:
|
626
|
-
return get_schema(schema_name, portal_vapp=self._vapp, key=self._key)
|
627
|
-
|
628
|
-
def get_schemas(self) -> dict:
|
629
|
-
return self.get("/profiles/").json()
|
630
|
-
|
631
|
-
def _uri(self, uri: str) -> str:
|
632
|
-
if not isinstance(uri, str) or not uri:
|
633
|
-
return "/"
|
634
|
-
if uri.lower().startswith("http://") or uri.lower().startswith("https://"):
|
635
|
-
return uri
|
636
|
-
uri = re.sub(r"/+", "/", uri)
|
637
|
-
return (self._server + ("/" if uri.startswith("/") else "") + uri) if self._server else uri
|
638
|
-
|
639
|
-
def _kwargs(self, **kwargs) -> dict:
|
640
|
-
result_kwargs = {"headers":
|
641
|
-
kwargs.get("headers", {"Content-type": "application/json", "Accept": "application/json"})}
|
642
|
-
if self._key_pair:
|
643
|
-
result_kwargs["auth"] = self._key_pair
|
644
|
-
if isinstance(timeout := kwargs.get("timeout"), int):
|
645
|
-
result_kwargs["timeout"] = timeout
|
646
|
-
return result_kwargs
|
647
|
-
|
648
|
-
def _response(self, response) -> Optional[RequestResponse]:
|
649
|
-
if response and isinstance(getattr(response.__class__, "json"), property):
|
650
|
-
class RequestResponseWrapper: # For consistency change json property to method.
|
651
|
-
def __init__(self, response, **kwargs):
|
652
|
-
super().__init__(**kwargs)
|
653
|
-
self._response = response
|
654
|
-
def __getattr__(self, attr): # noqa
|
655
|
-
return getattr(self._response, attr)
|
656
|
-
def json(self): # noqa
|
657
|
-
return self._response.json
|
658
|
-
response = RequestResponseWrapper(response)
|
659
|
-
return response
|
660
|
-
|
661
|
-
@staticmethod
|
662
|
-
def create_for_testing(ini_file: Optional[str] = None) -> PortalBase:
|
663
|
-
if isinstance(ini_file, str):
|
664
|
-
return Portal(Portal._create_testapp(ini_file))
|
665
|
-
minimal_ini_for_unit_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
|
666
|
-
with temporary_file(content=minimal_ini_for_unit_testing, suffix=".ini") as ini_file:
|
667
|
-
return Portal(Portal._create_testapp(ini_file))
|
668
|
-
|
669
|
-
@staticmethod
|
670
|
-
def create_for_testing_local(ini_file: Optional[str] = None) -> Portal:
|
671
|
-
if isinstance(ini_file, str) and ini_file:
|
672
|
-
return Portal(Portal._create_testapp(ini_file))
|
673
|
-
minimal_ini_for_testing_local = "\n".join([
|
674
|
-
"[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
|
675
|
-
"sqlalchemy.url = postgresql://postgres@localhost:5441/postgres?host=/tmp/snovault/pgdata",
|
676
|
-
"multiauth.groupfinder = encoded.authorization.smaht_groupfinder",
|
677
|
-
"multiauth.policies = auth0 session remoteuser accesskey",
|
678
|
-
"multiauth.policy.session.namespace = mailto",
|
679
|
-
"multiauth.policy.session.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
680
|
-
"multiauth.policy.session.base = pyramid.authentication.SessionAuthenticationPolicy",
|
681
|
-
"multiauth.policy.remoteuser.namespace = remoteuser",
|
682
|
-
"multiauth.policy.remoteuser.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
683
|
-
"multiauth.policy.remoteuser.base = pyramid.authentication.RemoteUserAuthenticationPolicy",
|
684
|
-
"multiauth.policy.accesskey.namespace = accesskey",
|
685
|
-
"multiauth.policy.accesskey.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
686
|
-
"multiauth.policy.accesskey.base = encoded.authentication.BasicAuthAuthenticationPolicy",
|
687
|
-
"multiauth.policy.accesskey.check = encoded.authentication.basic_auth_check",
|
688
|
-
"multiauth.policy.auth0.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
689
|
-
"multiauth.policy.auth0.namespace = auth0",
|
690
|
-
"multiauth.policy.auth0.base = encoded.authentication.Auth0AuthenticationPolicy"
|
691
|
-
])
|
692
|
-
with temporary_file(content=minimal_ini_for_testing_local, suffix=".ini") as minimal_ini_file:
|
693
|
-
return Portal(Portal._create_testapp(minimal_ini_file))
|
694
|
-
|
695
|
-
@staticmethod
|
696
|
-
def _create_testapp(value: Union[str, Router, TestApp] = "development.ini") -> TestApp:
|
697
|
-
"""
|
698
|
-
Creates and returns a TestApp. Refactored out of above loadxl code to consolidate at a
|
699
|
-
single point; also for use by the generate_local_access_key and view_local_object scripts.
|
700
|
-
"""
|
701
|
-
if isinstance(value, TestApp):
|
702
|
-
return value
|
703
|
-
app = value if isinstance(value, Router) else get_app(value, "app")
|
704
|
-
return TestApp(app, {"HTTP_ACCEPT": "application/json", "REMOTE_USER": "TEST"})
|
705
|
-
|
706
|
-
|
707
536
|
class Portal(PortalBase):
|
708
537
|
|
709
538
|
def __init__(self,
|
710
539
|
arg: Optional[Union[VirtualApp, TestApp, Router, Portal, dict, tuple, str]] = None,
|
711
|
-
env: Optional[str] = None, app: OrchestratedApp =
|
540
|
+
env: Optional[str] = None, app: OrchestratedApp = None, server: Optional[str] = None,
|
712
541
|
key: Optional[Union[dict, tuple]] = None,
|
713
542
|
portal: Optional[Union[VirtualApp, TestApp, Router, Portal, str]] = None,
|
714
543
|
data: Optional[dict] = None, schemas: Optional[List[dict]] = None) -> Optional[Portal]:
|
715
|
-
super(
|
544
|
+
super().__init__(arg, env=env, app=app, server=server, key=key, portal=portal)
|
716
545
|
if isinstance(arg, Portal) and not portal:
|
717
546
|
portal = arg
|
718
547
|
if isinstance(portal, Portal):
|
@@ -725,7 +554,7 @@ class Portal(PortalBase):
|
|
725
554
|
@lru_cache(maxsize=256)
|
726
555
|
def get_metadata(self, object_name: str) -> Optional[dict]:
|
727
556
|
try:
|
728
|
-
return super(
|
557
|
+
return super().get_metadata(object_name)
|
729
558
|
except Exception:
|
730
559
|
return None
|
731
560
|
|
@@ -740,7 +569,7 @@ class Portal(PortalBase):
|
|
740
569
|
|
741
570
|
@lru_cache(maxsize=1)
|
742
571
|
def get_schemas(self) -> dict:
|
743
|
-
schemas = super(
|
572
|
+
schemas = super().get_schemas()
|
744
573
|
if self._schemas:
|
745
574
|
schemas = copy.deepcopy(schemas)
|
746
575
|
for user_specified_schema in self._schemas:
|
@@ -748,42 +577,9 @@ class Portal(PortalBase):
|
|
748
577
|
schemas[user_specified_schema["title"]] = user_specified_schema
|
749
578
|
return schemas
|
750
579
|
|
751
|
-
def is_file_schema(self, schema_name: str) -> bool:
|
752
|
-
if super_type_map := self.get_schemas_super_type_map():
|
753
|
-
if file_super_type := super_type_map.get(FILE_SCHEMA_NAME):
|
754
|
-
return Schema.type_name(schema_name) in file_super_type
|
755
|
-
return False
|
756
|
-
|
757
580
|
@lru_cache(maxsize=1)
|
758
581
|
def get_schemas_super_type_map(self) -> dict:
|
759
|
-
|
760
|
-
Returns the "super type map" for all of the known schemas (via /profiles).
|
761
|
-
This is a dictionary of all types which have (one or more) sub-types whose value is
|
762
|
-
an array of all of those sub-types (direct and all descendents), in breadth first order.
|
763
|
-
"""
|
764
|
-
def breadth_first(super_type_map: dict, super_type_name: str) -> dict:
|
765
|
-
result = []
|
766
|
-
queue = deque(super_type_map.get(super_type_name, []))
|
767
|
-
while queue:
|
768
|
-
result.append(sub_type_name := queue.popleft())
|
769
|
-
if sub_type_name in super_type_map:
|
770
|
-
queue.extend(super_type_map[sub_type_name])
|
771
|
-
return result
|
772
|
-
if not (schemas := self.get_schemas()):
|
773
|
-
return {}
|
774
|
-
super_type_map = {}
|
775
|
-
for type_name in schemas:
|
776
|
-
if super_type_name := schemas[type_name].get("rdfs:subClassOf"):
|
777
|
-
super_type_name = super_type_name.replace("/profiles/", "").replace(".json", "")
|
778
|
-
if super_type_name != "Item":
|
779
|
-
if not super_type_map.get(super_type_name):
|
780
|
-
super_type_map[super_type_name] = [type_name]
|
781
|
-
elif type_name not in super_type_map[super_type_name]:
|
782
|
-
super_type_map[super_type_name].append(type_name)
|
783
|
-
super_type_map_flattened = {}
|
784
|
-
for super_type_name in super_type_map:
|
785
|
-
super_type_map_flattened[super_type_name] = breadth_first(super_type_map, super_type_name)
|
786
|
-
return super_type_map_flattened
|
582
|
+
return super().get_schemas_super_type_map()
|
787
583
|
|
788
584
|
def ref_exists(self, type_name: str, value: str) -> List[str]:
|
789
585
|
resolved = []
|
@@ -43,6 +43,7 @@ dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
|
|
43
43
|
dcicutils/misc_utils.py,sha256=jfyWDrHAlx2REun51i3igEApfEMAsmakRDo2VKUr0LQ,99818
|
44
44
|
dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
|
45
45
|
dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
|
46
|
+
dcicutils/portal_utils.py,sha256=s918K2f8ZuMD-2-H2GM6foiGe_YtzgG23xHtYe4gjlk,13021
|
46
47
|
dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
|
47
48
|
dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
|
48
49
|
dcicutils/qa_utils.py,sha256=TT0SiJWiuxYvbsIyhK9VO4uV_suxhB6CpuC4qPacCzQ,160208
|
@@ -55,14 +56,14 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
|
|
55
56
|
dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
|
56
57
|
dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
|
57
58
|
dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
|
58
|
-
dcicutils/structured_data.py,sha256=
|
59
|
+
dcicutils/structured_data.py,sha256=LGfNwJXYC5SHs92HA27XijbFZcCXfOatYVI1oo0tKu4,32120
|
59
60
|
dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
|
60
61
|
dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
|
61
62
|
dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
|
62
63
|
dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
|
63
64
|
dcicutils/zip_utils.py,sha256=0OXR0aLNwyLIZOzIFTM_5DOun7dxIv6TIZbFiithkO0,3276
|
64
|
-
dcicutils-8.4.0.
|
65
|
-
dcicutils-8.4.0.
|
66
|
-
dcicutils-8.4.0.
|
67
|
-
dcicutils-8.4.0.
|
68
|
-
dcicutils-8.4.0.
|
65
|
+
dcicutils-8.4.0.1b12.dist-info/LICENSE.txt,sha256=t0_-jIjqxNnymZoNJe-OltRIuuF8qfhN0ATlHyrUJPk,1102
|
66
|
+
dcicutils-8.4.0.1b12.dist-info/METADATA,sha256=f_Btb9g9ursc6-9DzbsrcbYRUMx4Te9rc2aotcDsphM,3315
|
67
|
+
dcicutils-8.4.0.1b12.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
68
|
+
dcicutils-8.4.0.1b12.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
|
69
|
+
dcicutils-8.4.0.1b12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|