benchling-sdk 1.9.0a4__py3-none-any.whl → 1.10.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.
- benchling_sdk/apps/canvas/__init__.py +0 -0
- benchling_sdk/apps/canvas/errors.py +14 -0
- benchling_sdk/apps/{helpers/canvas_helpers.py → canvas/framework.py} +129 -188
- benchling_sdk/apps/canvas/types.py +125 -0
- benchling_sdk/apps/config/__init__.py +0 -3
- benchling_sdk/apps/config/decryption_provider.py +1 -1
- benchling_sdk/apps/config/errors.py +38 -0
- benchling_sdk/apps/config/framework.py +343 -0
- benchling_sdk/apps/config/helpers.py +157 -0
- benchling_sdk/apps/config/{mock_dependencies.py → mock_config.py} +78 -99
- benchling_sdk/apps/config/types.py +36 -0
- benchling_sdk/apps/framework.py +49 -338
- benchling_sdk/apps/helpers/webhook_helpers.py +2 -2
- benchling_sdk/apps/status/__init__.py +0 -0
- benchling_sdk/apps/status/errors.py +85 -0
- benchling_sdk/apps/{helpers/session_helpers.py → status/framework.py} +58 -167
- benchling_sdk/apps/status/helpers.py +20 -0
- benchling_sdk/apps/status/types.py +45 -0
- benchling_sdk/apps/types.py +3 -0
- benchling_sdk/errors.py +4 -4
- benchling_sdk/helpers/retry_helpers.py +3 -1
- benchling_sdk/models/__init__.py +44 -0
- benchling_sdk/services/v2/beta/{v2_beta_dataset_service.py → v2_beta_data_frame_service.py} +126 -116
- benchling_sdk/services/v2/stable/assay_result_service.py +18 -0
- benchling_sdk/services/v2/v2_beta_service.py +11 -11
- {benchling_sdk-1.9.0a4.dist-info → benchling_sdk-1.10.0.dist-info}/METADATA +4 -4
- {benchling_sdk-1.9.0a4.dist-info → benchling_sdk-1.10.0.dist-info}/RECORD +30 -21
- benchling_sdk/apps/config/dependencies.py +0 -1085
- benchling_sdk/apps/config/scalars.py +0 -226
- benchling_sdk/apps/helpers/config_helpers.py +0 -409
- /benchling_sdk/apps/{helpers → config}/cryptography_helpers.py +0 -0
- {benchling_sdk-1.9.0a4.dist-info → benchling_sdk-1.10.0.dist-info}/LICENSE +0 -0
- {benchling_sdk-1.9.0a4.dist-info → benchling_sdk-1.10.0.dist-info}/WHEEL +0 -0
@@ -1,1085 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from abc import ABC
|
4
|
-
from dataclasses import dataclass
|
5
|
-
from typing import cast, Dict, Generic, List, Optional, Protocol, Tuple, Type, TypeVar, Union
|
6
|
-
|
7
|
-
from benchling_api_client.v2.extensions import UnknownType
|
8
|
-
from ordered_set import OrderedSet
|
9
|
-
|
10
|
-
from benchling_sdk.apps.config.decryption_provider import BaseDecryptionProvider
|
11
|
-
from benchling_sdk.apps.config.scalars import (
|
12
|
-
DEFAULT_SCALAR_DEFINITIONS,
|
13
|
-
scalar_definition_from_field_type,
|
14
|
-
ScalarConfigItemType,
|
15
|
-
ScalarDefinition,
|
16
|
-
ScalarType,
|
17
|
-
)
|
18
|
-
from benchling_sdk.benchling import Benchling
|
19
|
-
from benchling_sdk.models import (
|
20
|
-
AppConfigItem,
|
21
|
-
ArrayElementAppConfigItem,
|
22
|
-
BooleanAppConfigItem,
|
23
|
-
DateAppConfigItem,
|
24
|
-
DatetimeAppConfigItem,
|
25
|
-
EntitySchemaAppConfigItem,
|
26
|
-
Field,
|
27
|
-
FieldAppConfigItem,
|
28
|
-
FloatAppConfigItem,
|
29
|
-
GenericApiIdentifiedAppConfigItem,
|
30
|
-
InaccessibleResource,
|
31
|
-
IntegerAppConfigItem,
|
32
|
-
JsonAppConfigItem,
|
33
|
-
LinkedAppConfigResourceSummary,
|
34
|
-
ListAppConfigurationItemsSort,
|
35
|
-
SecureTextAppConfigItem,
|
36
|
-
TextAppConfigItem,
|
37
|
-
)
|
38
|
-
|
39
|
-
|
40
|
-
class MissingDependencyError(Exception):
|
41
|
-
"""
|
42
|
-
Missing dependency error.
|
43
|
-
|
44
|
-
Indicates a dependency was missing from app config.
|
45
|
-
For instance, no dependency with that name was in the list.
|
46
|
-
"""
|
47
|
-
|
48
|
-
pass
|
49
|
-
|
50
|
-
|
51
|
-
class UnsupportedDependencyError(Exception):
|
52
|
-
"""
|
53
|
-
Unsupported dependency error.
|
54
|
-
|
55
|
-
The manifest and configuration specified a dependency which the SDK is incapable of handling yet.
|
56
|
-
"""
|
57
|
-
|
58
|
-
pass
|
59
|
-
|
60
|
-
|
61
|
-
class MissingScalarDefinitionError(Exception):
|
62
|
-
"""
|
63
|
-
Missing scalar definition error.
|
64
|
-
|
65
|
-
The manifest and configuration specified a scalar type which the SDK does not know how to translate
|
66
|
-
to Python values yet.
|
67
|
-
"""
|
68
|
-
|
69
|
-
pass
|
70
|
-
|
71
|
-
|
72
|
-
class InaccessibleAppConfigResourceError(Exception):
|
73
|
-
"""
|
74
|
-
Inaccessible app config resource error.
|
75
|
-
|
76
|
-
A resource was linked in app config, but the permissions do not allow access to it. Most likely happens
|
77
|
-
when an app lacks the necessary permissions.
|
78
|
-
"""
|
79
|
-
|
80
|
-
pass
|
81
|
-
|
82
|
-
|
83
|
-
ConfigItemPath = Tuple[str, ...]
|
84
|
-
|
85
|
-
# Everything from AppConfigItem except UnknownType
|
86
|
-
ConfigurationReference = Union[
|
87
|
-
ArrayElementAppConfigItem,
|
88
|
-
DateAppConfigItem,
|
89
|
-
DatetimeAppConfigItem,
|
90
|
-
JsonAppConfigItem,
|
91
|
-
EntitySchemaAppConfigItem,
|
92
|
-
FieldAppConfigItem,
|
93
|
-
BooleanAppConfigItem,
|
94
|
-
IntegerAppConfigItem,
|
95
|
-
FloatAppConfigItem,
|
96
|
-
GenericApiIdentifiedAppConfigItem,
|
97
|
-
SecureTextAppConfigItem,
|
98
|
-
TextAppConfigItem,
|
99
|
-
]
|
100
|
-
|
101
|
-
ConfigWithLinkedResource = Union[
|
102
|
-
EntitySchemaAppConfigItem,
|
103
|
-
FieldAppConfigItem,
|
104
|
-
GenericApiIdentifiedAppConfigItem,
|
105
|
-
]
|
106
|
-
|
107
|
-
ScalarConfigReference = Union[
|
108
|
-
BooleanAppConfigItem,
|
109
|
-
DateAppConfigItem,
|
110
|
-
DatetimeAppConfigItem,
|
111
|
-
FloatAppConfigItem,
|
112
|
-
IntegerAppConfigItem,
|
113
|
-
JsonAppConfigItem,
|
114
|
-
SecureTextAppConfigItem,
|
115
|
-
TextAppConfigItem,
|
116
|
-
]
|
117
|
-
|
118
|
-
D = TypeVar("D", bound="BaseDependencies")
|
119
|
-
F = TypeVar("F", bound="BaseField")
|
120
|
-
|
121
|
-
|
122
|
-
class ConfigProvider(Protocol):
|
123
|
-
"""
|
124
|
-
Config provider.
|
125
|
-
|
126
|
-
Provides a BenchlingAppConfiguration.
|
127
|
-
"""
|
128
|
-
|
129
|
-
def config(self) -> List[ConfigurationReference]:
|
130
|
-
"""Implement to provide a Benchling app configuration."""
|
131
|
-
pass
|
132
|
-
|
133
|
-
|
134
|
-
class BenchlingConfigProvider(ConfigProvider):
|
135
|
-
"""
|
136
|
-
Benchling Config provider.
|
137
|
-
|
138
|
-
Provides a BenchlingAppConfiguration retrieved from Benchling's API.
|
139
|
-
"""
|
140
|
-
|
141
|
-
_client: Benchling
|
142
|
-
_app_id: str
|
143
|
-
|
144
|
-
def __init__(self, client: Benchling, app_id: str):
|
145
|
-
"""
|
146
|
-
Initialize Benchling Config Provider.
|
147
|
-
|
148
|
-
:param client: A configured Benchling instance for making API calls.
|
149
|
-
:param app_id: The app_id from which to retrieve configuration.
|
150
|
-
"""
|
151
|
-
self._client = client
|
152
|
-
self._app_id = app_id
|
153
|
-
|
154
|
-
def config(self) -> List[ConfigurationReference]:
|
155
|
-
"""Provide a Benchling app configuration from Benchling's APIs."""
|
156
|
-
app_pages = self._client.apps.list_app_configuration_items(
|
157
|
-
app_id=self._app_id,
|
158
|
-
page_size=100,
|
159
|
-
sort=ListAppConfigurationItemsSort.CREATEDATASC,
|
160
|
-
)
|
161
|
-
|
162
|
-
# Eager load all config items for now since we don't yet have a way of lazily querying by path
|
163
|
-
all_config_pages = list(app_pages)
|
164
|
-
# Punt on UnknownType for now as apps using manifests with new types could lead to unpredictable results
|
165
|
-
all_config_items = [
|
166
|
-
_supported_config_item(config_item) for page in all_config_pages for config_item in page
|
167
|
-
]
|
168
|
-
|
169
|
-
return all_config_items
|
170
|
-
|
171
|
-
|
172
|
-
class StaticConfigProvider(ConfigProvider):
|
173
|
-
"""
|
174
|
-
Static Config provider.
|
175
|
-
|
176
|
-
Provides a BenchlingAppConfiguration from a static declaration. Useful for mocking or testing.
|
177
|
-
"""
|
178
|
-
|
179
|
-
_configuration_items: List[ConfigurationReference]
|
180
|
-
|
181
|
-
def __init__(self, configuration_items: List[ConfigurationReference]):
|
182
|
-
"""
|
183
|
-
Initialize Static Config Provider.
|
184
|
-
|
185
|
-
:param configuration_items: The configuration items to return.
|
186
|
-
"""
|
187
|
-
self._configuration_items = configuration_items
|
188
|
-
|
189
|
-
def config(self) -> List[ConfigurationReference]:
|
190
|
-
"""Provide Benchling app configuration items from a static reference."""
|
191
|
-
return self._configuration_items
|
192
|
-
|
193
|
-
|
194
|
-
class DependencyLinkStore(object):
|
195
|
-
"""
|
196
|
-
Dependency Link Store.
|
197
|
-
|
198
|
-
Marshalls an app configuration from the configuration provider into an indexable structure.
|
199
|
-
Only retrieves app configuration once unless its cache is invalidated.
|
200
|
-
"""
|
201
|
-
|
202
|
-
_configuration_provider: ConfigProvider
|
203
|
-
_configuration: Optional[List[ConfigurationReference]] = None
|
204
|
-
_configuration_map: Optional[Dict[ConfigItemPath, ConfigurationReference]] = None
|
205
|
-
_array_path_row_names: Dict[Tuple[str, ...], OrderedSet[str]] = dict()
|
206
|
-
|
207
|
-
def __init__(self, configuration_provider: ConfigProvider):
|
208
|
-
"""
|
209
|
-
Initialize Dependency Link Store.
|
210
|
-
|
211
|
-
:param configuration_provider: A ConfigProvider that will be invoked to provide the
|
212
|
-
underlying config from which to organize dependency links.
|
213
|
-
"""
|
214
|
-
self._configuration_provider = configuration_provider
|
215
|
-
self._array_path_row_names = dict()
|
216
|
-
|
217
|
-
@classmethod
|
218
|
-
def from_app(cls, client: Benchling, app_id: str) -> DependencyLinkStore:
|
219
|
-
"""
|
220
|
-
From App.
|
221
|
-
|
222
|
-
Instantiate a DependencyLinkStore from an app_id and a configured Benchling instance. Preferred to
|
223
|
-
using the class's constructor.
|
224
|
-
"""
|
225
|
-
config_provider = BenchlingConfigProvider(client, app_id)
|
226
|
-
return cls(config_provider)
|
227
|
-
|
228
|
-
@property
|
229
|
-
def configuration(self) -> List[ConfigurationReference]:
|
230
|
-
"""
|
231
|
-
Get the underlying configuration.
|
232
|
-
|
233
|
-
Return the raw, stored configuration. Can be used if the provided accessors are inadequate
|
234
|
-
to find particular configuration items.
|
235
|
-
"""
|
236
|
-
if not self._configuration:
|
237
|
-
self._configuration = self._configuration_provider.config()
|
238
|
-
return self._configuration
|
239
|
-
|
240
|
-
@property
|
241
|
-
def configuration_path_map(self) -> Dict[ConfigItemPath, ConfigurationReference]:
|
242
|
-
"""
|
243
|
-
Config links.
|
244
|
-
|
245
|
-
Return a map of configuration item paths to their corresponding configuration items.
|
246
|
-
"""
|
247
|
-
if not self._configuration_map:
|
248
|
-
self._configuration_map = {tuple(item.path): item for item in self.configuration}
|
249
|
-
return self._configuration_map
|
250
|
-
|
251
|
-
def config_by_path(self, path: List[str]) -> Optional[ConfigurationReference]:
|
252
|
-
"""
|
253
|
-
Config by path.
|
254
|
-
|
255
|
-
Find an app config item by its exact path match, if it exists. Does not search partial paths.
|
256
|
-
"""
|
257
|
-
# Since we eager load all config now, we know that missing path means it's not configured in Benchling
|
258
|
-
# Later if we support lazy loading, we'll need to differentiate what's in our cache versus missing
|
259
|
-
return self.configuration_path_map.get(tuple(path))
|
260
|
-
|
261
|
-
def config_keys_by_path(self, path: List[str]) -> OrderedSet[str]:
|
262
|
-
"""
|
263
|
-
Config keys by path.
|
264
|
-
|
265
|
-
Find a set of app config keys at the specified path, if any. Does not return keys that are nested
|
266
|
-
beyond the current level.
|
267
|
-
|
268
|
-
For instance, given paths:
|
269
|
-
["One", "Two"]
|
270
|
-
["One", "Two", "Three"]
|
271
|
-
["One", "Two", "Four"]
|
272
|
-
["One", "Two", "Three", "Five"]
|
273
|
-
["Zero", "One", "Two", "Three"]
|
274
|
-
|
275
|
-
The expected return from this method when path=["One", "Two"] is a set {"Three", "Four"}.
|
276
|
-
"""
|
277
|
-
# Convert path to tuple, as list is not hashable for dict keys
|
278
|
-
path_tuple = tuple(path)
|
279
|
-
if path_tuple not in self._array_path_row_names:
|
280
|
-
self._array_path_row_names[path_tuple] = OrderedSet(
|
281
|
-
[
|
282
|
-
config_item.path[len(path)]
|
283
|
-
# Use the list instead of configuration_map to preserve order
|
284
|
-
for config_item in self.configuration
|
285
|
-
# The +1 is the name of the array row
|
286
|
-
if len(config_item.path) >= len(path) + 1
|
287
|
-
# Ignoring flake8 error E203 because black keeps putting in whitespace padding :
|
288
|
-
and config_item.path[0 : len(path_tuple)] == path # noqa: E203
|
289
|
-
and config_item.value is not None
|
290
|
-
]
|
291
|
-
)
|
292
|
-
return self._array_path_row_names[path_tuple]
|
293
|
-
|
294
|
-
def invalidate_cache(self) -> None:
|
295
|
-
"""
|
296
|
-
Invalidate Cache.
|
297
|
-
|
298
|
-
Will force retrieval of configuration from the ConfigProvider the next time the link store is accessed.
|
299
|
-
"""
|
300
|
-
self._configuration = None
|
301
|
-
self._configuration_map = None
|
302
|
-
self._array_path_row_names = dict()
|
303
|
-
|
304
|
-
|
305
|
-
class HasAppConfigItem(Protocol):
|
306
|
-
"""
|
307
|
-
Has App Config Item.
|
308
|
-
|
309
|
-
A mixin for typing to assert that a class has an optional app config item attribute.
|
310
|
-
"""
|
311
|
-
|
312
|
-
@property
|
313
|
-
def path(self) -> List[str]:
|
314
|
-
"""Return the path requested by the manifest."""
|
315
|
-
pass
|
316
|
-
|
317
|
-
@property
|
318
|
-
def config_item(self) -> Optional[ConfigurationReference]:
|
319
|
-
"""Return the underlying app config item, if present."""
|
320
|
-
pass
|
321
|
-
|
322
|
-
|
323
|
-
class HasApiIdentifiedAppConfigItem(Protocol):
|
324
|
-
"""
|
325
|
-
Has Api Identified App Config Item.
|
326
|
-
|
327
|
-
A mixin for typing to assert that a class has an optional app config item attribute.
|
328
|
-
That app config item must have a linked_resource property.
|
329
|
-
"""
|
330
|
-
|
331
|
-
@property
|
332
|
-
def path(self) -> List[str]:
|
333
|
-
"""Return the path requested by the manifest."""
|
334
|
-
pass
|
335
|
-
|
336
|
-
@property
|
337
|
-
def config_item(self) -> Optional[ConfigWithLinkedResource]:
|
338
|
-
"""Return the underlying app config item, if present. App config item must have linked_resource."""
|
339
|
-
pass
|
340
|
-
|
341
|
-
|
342
|
-
class HasScalarDefinition(Protocol):
|
343
|
-
"""
|
344
|
-
Has Scalar Definition.
|
345
|
-
|
346
|
-
A mixin for typing to assert that a particular class has scalar attributes.
|
347
|
-
"""
|
348
|
-
|
349
|
-
@property
|
350
|
-
def path(self) -> List[str]:
|
351
|
-
"""Return the path requested by the manifest."""
|
352
|
-
pass
|
353
|
-
|
354
|
-
@property
|
355
|
-
def config_item(self) -> Optional[ConfigurationReference]:
|
356
|
-
"""Return the underlying app config item, if present."""
|
357
|
-
pass
|
358
|
-
|
359
|
-
@property
|
360
|
-
def definition(self) -> Optional[ScalarDefinition]:
|
361
|
-
"""Return the scalar definition, allowing for conversion to Python types."""
|
362
|
-
pass
|
363
|
-
|
364
|
-
|
365
|
-
class HasConfigWithDecryptionProvider(Protocol):
|
366
|
-
"""
|
367
|
-
Has Config With Decryption Provider.
|
368
|
-
|
369
|
-
A mixin for typing to assert that a particular class has a decryption provider and config.
|
370
|
-
"""
|
371
|
-
|
372
|
-
@property
|
373
|
-
def path(self) -> List[str]:
|
374
|
-
"""Return the path requested by the manifest."""
|
375
|
-
pass
|
376
|
-
|
377
|
-
@property
|
378
|
-
def config_item(self) -> Optional[ConfigurationReference]:
|
379
|
-
"""Return the underlying app config item, if present."""
|
380
|
-
pass
|
381
|
-
|
382
|
-
@property
|
383
|
-
def decryption_provider(self) -> Optional[BaseDecryptionProvider]:
|
384
|
-
"""Return the decryption provider."""
|
385
|
-
pass
|
386
|
-
|
387
|
-
|
388
|
-
class RequiredLinkedResourceDependencyMixin:
|
389
|
-
"""
|
390
|
-
Required Linked Resource Dependency Mixin.
|
391
|
-
|
392
|
-
A mixin for easily accessing attributes from linked_resource for an app config item which is required and
|
393
|
-
should always be present. Should only be mixed in with HasApiIdentifiedAppConfigItem
|
394
|
-
"""
|
395
|
-
|
396
|
-
@property
|
397
|
-
def id(self: HasApiIdentifiedAppConfigItem) -> str:
|
398
|
-
"""Return the API ID of the linked configuration."""
|
399
|
-
assert (
|
400
|
-
self.config_item is not None and self.config_item.value is not None
|
401
|
-
), f"The app config item {self.path} is not set in Benchling"
|
402
|
-
# config_item.value and linked_resource.id are the same for now,
|
403
|
-
# so we can eschew inaccessible resource checking
|
404
|
-
return self.config_item.value
|
405
|
-
|
406
|
-
@property
|
407
|
-
def name(self: HasApiIdentifiedAppConfigItem) -> str:
|
408
|
-
"""Return the name of the linked configuration.
|
409
|
-
|
410
|
-
Raises InaccessibleAppConfigResourceError if the app does not have permission to the linked resource.
|
411
|
-
"""
|
412
|
-
assert (
|
413
|
-
self.config_item is not None and self.config_item.value is not None
|
414
|
-
), f"The app config item {self.path} is not set in Benchling"
|
415
|
-
if isinstance(self.config_item.linked_resource, InaccessibleResource):
|
416
|
-
raise InaccessibleAppConfigResourceError(
|
417
|
-
f'No permissions to the linked resource "{self.config_item.value}" referenced by {self.path}'
|
418
|
-
)
|
419
|
-
# Required for type checking
|
420
|
-
assert isinstance(
|
421
|
-
self.config_item.linked_resource, LinkedAppConfigResourceSummary
|
422
|
-
), f"Expected linked resource from app config item but got {type(self.config_item.linked_resource)}"
|
423
|
-
return self.config_item.linked_resource.name
|
424
|
-
|
425
|
-
|
426
|
-
class OptionalLinkedResourceDependencyMixin:
|
427
|
-
"""
|
428
|
-
Optional Linked Resource Dependency Mixin.
|
429
|
-
|
430
|
-
A mixin for easily accessing attributes from linked_resource for an app config item which is optional and
|
431
|
-
may not be present. Should only be mixed in with HasApiIdentifiedAppConfigItem
|
432
|
-
"""
|
433
|
-
|
434
|
-
@property
|
435
|
-
def id(self: HasApiIdentifiedAppConfigItem) -> Optional[str]:
|
436
|
-
"""Return the API ID of the linked configuration, if present."""
|
437
|
-
# config_item.value and linked_resource.id are the same for now,
|
438
|
-
# so we can eschew inaccessible resource checking
|
439
|
-
if self.config_item is not None and self.config_item.value is not None:
|
440
|
-
return self.config_item.value
|
441
|
-
return None
|
442
|
-
|
443
|
-
@property
|
444
|
-
def name(self: HasApiIdentifiedAppConfigItem) -> Optional[str]:
|
445
|
-
"""Return the name of the linked configuration, if present.
|
446
|
-
|
447
|
-
Raises InaccessibleAppConfigResourceError if the app does not have permission to the linked resource.
|
448
|
-
"""
|
449
|
-
if self.config_item is not None and self.config_item.value is not None:
|
450
|
-
if isinstance(self.config_item.linked_resource, InaccessibleResource):
|
451
|
-
raise InaccessibleAppConfigResourceError(
|
452
|
-
f'No permissions to the linked resource "{self.config_item.value}" referenced by {self.path}'
|
453
|
-
)
|
454
|
-
# Required for type checking
|
455
|
-
assert isinstance(
|
456
|
-
self.config_item.linked_resource, LinkedAppConfigResourceSummary
|
457
|
-
), f"Expected linked resource from app config item but got {type(self.config_item.linked_resource)}"
|
458
|
-
return self.config_item.linked_resource.name
|
459
|
-
return None
|
460
|
-
|
461
|
-
|
462
|
-
class OptionalValueMixin:
|
463
|
-
"""
|
464
|
-
Optional Value Mixin.
|
465
|
-
|
466
|
-
A mixin for accessing a value which is optional and may not be present. Should
|
467
|
-
only be mixed in with HasAppConfigItem or another class that provides the `self.config_item` attribute.
|
468
|
-
"""
|
469
|
-
|
470
|
-
@property
|
471
|
-
def value(self: HasAppConfigItem) -> Optional[str]:
|
472
|
-
"""Return the value of the app config item, if present."""
|
473
|
-
if self.config_item and self.config_item.value:
|
474
|
-
return str(self.config_item.value)
|
475
|
-
return None
|
476
|
-
|
477
|
-
|
478
|
-
class RequiredValueMixin:
|
479
|
-
"""
|
480
|
-
Required Value Mixin.
|
481
|
-
|
482
|
-
A mixin for accessing a value which is required and should always be present. Should
|
483
|
-
only be mixed in with HasAppConfigItem or another class that provides the `self.config_item` attribute.
|
484
|
-
"""
|
485
|
-
|
486
|
-
@property
|
487
|
-
def value(self: HasAppConfigItem) -> str:
|
488
|
-
"""Return the value of the app config item."""
|
489
|
-
assert (
|
490
|
-
self.config_item is not None and self.config_item.value is not None
|
491
|
-
), f"The app config item {self.path} is not set in Benchling"
|
492
|
-
return str(self.config_item.value)
|
493
|
-
|
494
|
-
|
495
|
-
class RequiredScalarDependencyMixin(Generic[ScalarType]):
|
496
|
-
"""
|
497
|
-
Require Scalar Config.
|
498
|
-
|
499
|
-
A mixin for accessing a scalar config which is required and should always be present.
|
500
|
-
Should only be mixed in with HasScalarDefinition.
|
501
|
-
"""
|
502
|
-
|
503
|
-
@property
|
504
|
-
def value(self: HasScalarDefinition) -> ScalarType:
|
505
|
-
"""Return the value of the scalar."""
|
506
|
-
if self.definition:
|
507
|
-
assert (
|
508
|
-
self.config_item is not None and self.config_item.value is not None
|
509
|
-
), f"The app config item {self.path} is not set in Benchling"
|
510
|
-
optional_typed_value = self.definition.from_str(value=str(self.config_item.value))
|
511
|
-
assert optional_typed_value is not None
|
512
|
-
return optional_typed_value
|
513
|
-
raise MissingScalarDefinitionError(f"No definition registered for scalar config {self.path}")
|
514
|
-
|
515
|
-
@property
|
516
|
-
def value_str(self: HasScalarDefinition) -> str:
|
517
|
-
"""Return the value of the scalar as a string."""
|
518
|
-
assert (
|
519
|
-
self.config_item is not None and self.config_item.value is not None
|
520
|
-
), f"The app config item {self.path} is not set in Benchling"
|
521
|
-
# Booleans are currently specified as str in the spec but are bool at runtime in JSON
|
522
|
-
return str(self.config_item.value)
|
523
|
-
|
524
|
-
|
525
|
-
class OptionalScalarDependencyMixin(Generic[ScalarType]):
|
526
|
-
"""
|
527
|
-
Optional Scalar Config.
|
528
|
-
|
529
|
-
A mixin for accessing a scalar config which is optional and may not be present.
|
530
|
-
Should only be mixed in with HasScalarDefinition.
|
531
|
-
"""
|
532
|
-
|
533
|
-
@property
|
534
|
-
def value(self: HasScalarDefinition) -> Optional[ScalarType]:
|
535
|
-
"""Return the value of the scalar, if present."""
|
536
|
-
if self.config_item and self.config_item.value:
|
537
|
-
if self.definition:
|
538
|
-
return self.definition.from_str(value=str(self.config_item.value))
|
539
|
-
raise MissingScalarDefinitionError(f"No definition registered for scalar config {self.path}")
|
540
|
-
return None
|
541
|
-
|
542
|
-
@property
|
543
|
-
def value_str(self: HasScalarDefinition) -> Optional[str]:
|
544
|
-
"""Return the value of the scalar as a string, if present."""
|
545
|
-
if self.config_item and self.config_item.value:
|
546
|
-
return str(self.config_item.value)
|
547
|
-
return None
|
548
|
-
|
549
|
-
|
550
|
-
class RequiredSecureTextDependencyMixin(RequiredScalarDependencyMixin[str]):
|
551
|
-
"""
|
552
|
-
Require Secure Text.
|
553
|
-
|
554
|
-
A mixin for accessing a secure text config which is required and should always be present.
|
555
|
-
Should only be mixed in with SecureTextConfig.
|
556
|
-
"""
|
557
|
-
|
558
|
-
def decrypted_value(self: HasConfigWithDecryptionProvider) -> str:
|
559
|
-
"""
|
560
|
-
Decrypted value.
|
561
|
-
|
562
|
-
Decrypts a secure_text dependency's encrypted value into plain text.
|
563
|
-
"""
|
564
|
-
assert (
|
565
|
-
self.config_item is not None and self.config_item.value is not None
|
566
|
-
), f"The app config item {self.path} is not set in Benchling"
|
567
|
-
assert (
|
568
|
-
self.decryption_provider is not None
|
569
|
-
), f"The app config item {self.config_item} cannot be decrypted because no DecryptionProvider was set"
|
570
|
-
return self.decryption_provider.decrypt(str(self.config_item.value))
|
571
|
-
|
572
|
-
|
573
|
-
class OptionalSecureTextDependencyMixin(OptionalScalarDependencyMixin[str]):
|
574
|
-
"""
|
575
|
-
Optional Secure Text.
|
576
|
-
|
577
|
-
A mixin for accessing a secure text config which is optional and may not be present.
|
578
|
-
Should only be mixed in with SecureTextConfig.
|
579
|
-
"""
|
580
|
-
|
581
|
-
def decrypted_value(self: HasConfigWithDecryptionProvider) -> Optional[str]:
|
582
|
-
"""
|
583
|
-
Decrypted value.
|
584
|
-
|
585
|
-
Decrypts a secure_text dependency's encrypted value into plain text, if present.
|
586
|
-
"""
|
587
|
-
if self.config_item and self.config_item.value:
|
588
|
-
assert (
|
589
|
-
self.decryption_provider is not None
|
590
|
-
), f"The app config item {self.config_item} cannot be decrypted because no DecryptionProvider was set"
|
591
|
-
return self.decryption_provider.decrypt(str(self.config_item.value))
|
592
|
-
return None
|
593
|
-
|
594
|
-
|
595
|
-
@dataclass
|
596
|
-
class BaseConfigNode:
|
597
|
-
"""
|
598
|
-
Base Config Node.
|
599
|
-
|
600
|
-
A node in a graph of related config items, referencing its parent.
|
601
|
-
|
602
|
-
All nodes should have a parent, which may be BaseDependencies, but not all nodes represent an AppConfigItem
|
603
|
-
in Benchling.
|
604
|
-
"""
|
605
|
-
|
606
|
-
parent: Union[BaseDependencies, BaseConfigNode]
|
607
|
-
|
608
|
-
def context(self) -> BaseDependencies:
|
609
|
-
"""Return the dependency class at the root of the dependency graph."""
|
610
|
-
if isinstance(self.parent, BaseDependencies):
|
611
|
-
return self.parent
|
612
|
-
return self.parent.context()
|
613
|
-
|
614
|
-
def full_path(self) -> List[str]:
|
615
|
-
"""Return the full path of the current node, inheriting from all parents."""
|
616
|
-
parent_path = self.parent.full_path() if isinstance(self.parent, BaseConfigNode) else []
|
617
|
-
# Fields and options classes typically don't define paths
|
618
|
-
last_path = getattr(self, "path", [])
|
619
|
-
return parent_path + last_path
|
620
|
-
|
621
|
-
|
622
|
-
@dataclass
|
623
|
-
class BaseConfigItem(BaseConfigNode):
|
624
|
-
"""
|
625
|
-
Base Config Item.
|
626
|
-
|
627
|
-
A reference to any config item.
|
628
|
-
"""
|
629
|
-
|
630
|
-
config_item: Optional[ConfigurationReference]
|
631
|
-
|
632
|
-
|
633
|
-
@dataclass
|
634
|
-
class ApiConfigItem(BaseConfigItem):
|
635
|
-
"""
|
636
|
-
API Config Item.
|
637
|
-
|
638
|
-
A reference to a config item for a Benchling object referencable by the API.
|
639
|
-
"""
|
640
|
-
|
641
|
-
config_item: Optional[ConfigWithLinkedResource]
|
642
|
-
|
643
|
-
|
644
|
-
class BaseField(ABC, Field, Generic[ScalarType]):
|
645
|
-
"""
|
646
|
-
Base Field.
|
647
|
-
|
648
|
-
Provides additional accessors on top of the OpenAPI Field model.
|
649
|
-
"""
|
650
|
-
|
651
|
-
@classmethod
|
652
|
-
def from_field(cls: Type[F], field: Optional[Field]) -> F:
|
653
|
-
"""
|
654
|
-
From Field.
|
655
|
-
|
656
|
-
Create a new instance from an existing field.
|
657
|
-
"""
|
658
|
-
if field:
|
659
|
-
return cls(
|
660
|
-
value=field.value,
|
661
|
-
display_value=field.display_value,
|
662
|
-
is_multi=field.is_multi,
|
663
|
-
text_value=field.text_value,
|
664
|
-
type=field.type,
|
665
|
-
)
|
666
|
-
return cls(value=None)
|
667
|
-
|
668
|
-
@property
|
669
|
-
def scalar_definition(self) -> ScalarDefinition[ScalarType]:
|
670
|
-
"""
|
671
|
-
Scalar Definition.
|
672
|
-
|
673
|
-
Returns a scalar definition for parsing a concrete type from a field.
|
674
|
-
Override to implement custom deserialization.
|
675
|
-
"""
|
676
|
-
return scalar_definition_from_field_type(self.type)
|
677
|
-
|
678
|
-
|
679
|
-
class RequiredField(BaseField, Generic[ScalarType]):
|
680
|
-
"""
|
681
|
-
Required Field.
|
682
|
-
|
683
|
-
A decorator class providing typed accessors for an underlying Field.
|
684
|
-
Use with required, single valued fields.
|
685
|
-
"""
|
686
|
-
|
687
|
-
@dataclass
|
688
|
-
class _RequiredFieldTyped:
|
689
|
-
base_field: RequiredField
|
690
|
-
|
691
|
-
@property
|
692
|
-
def value(self) -> ScalarType:
|
693
|
-
"""
|
694
|
-
Typed Value.
|
695
|
-
|
696
|
-
Returns the value of the field typed as it's specified in an app manifest.
|
697
|
-
"""
|
698
|
-
# Can be None in the case of someone changing config
|
699
|
-
typed_value = self.base_field.scalar_definition.from_str(value=str(self.base_field.value))
|
700
|
-
assert typed_value is not None
|
701
|
-
return typed_value
|
702
|
-
|
703
|
-
@property
|
704
|
-
def display_value(self) -> str:
|
705
|
-
"""
|
706
|
-
Display Value.
|
707
|
-
|
708
|
-
Return the field's display value as a string.
|
709
|
-
"""
|
710
|
-
assert self.base_field.display_value is not None
|
711
|
-
return self.base_field.display_value
|
712
|
-
|
713
|
-
@property
|
714
|
-
def typed(self) -> _RequiredFieldTyped:
|
715
|
-
"""
|
716
|
-
Typed.
|
717
|
-
|
718
|
-
Return a reference to a typed field with typesafe accessors.
|
719
|
-
"""
|
720
|
-
return self._RequiredFieldTyped(self)
|
721
|
-
|
722
|
-
|
723
|
-
class OptionalField(BaseField, Generic[ScalarType]):
|
724
|
-
"""
|
725
|
-
Optional Field.
|
726
|
-
|
727
|
-
A decorator class providing typed accessors for an underlying Field.
|
728
|
-
Use with optional, single valued fields.
|
729
|
-
"""
|
730
|
-
|
731
|
-
@dataclass
|
732
|
-
class _OptionalFieldTyped:
|
733
|
-
base_field: OptionalField
|
734
|
-
|
735
|
-
@property
|
736
|
-
def value(self) -> Optional[ScalarType]:
|
737
|
-
"""
|
738
|
-
Typed Value.
|
739
|
-
|
740
|
-
Returns the value of the field typed as it's specified in an app manifest, if the field is present.
|
741
|
-
"""
|
742
|
-
if self.base_field.value:
|
743
|
-
field_value = str(self.base_field.value)
|
744
|
-
return self.base_field.scalar_definition.from_str(value=field_value)
|
745
|
-
return None
|
746
|
-
|
747
|
-
@property
|
748
|
-
def display_value(self) -> Optional[str]:
|
749
|
-
"""
|
750
|
-
Display Value.
|
751
|
-
|
752
|
-
Return the field's display value as a string, if present.
|
753
|
-
"""
|
754
|
-
# Check to ensure linked before display_value, which will raise NotPresentError on unlinked
|
755
|
-
if self.base_field.value is None:
|
756
|
-
return None
|
757
|
-
return None if self.base_field.display_value == "" else self.base_field.display_value
|
758
|
-
|
759
|
-
@property
|
760
|
-
def typed(self) -> _OptionalFieldTyped:
|
761
|
-
"""
|
762
|
-
Typed.
|
763
|
-
|
764
|
-
Return a reference to a typed field with typesafe accessors.
|
765
|
-
"""
|
766
|
-
return self._OptionalFieldTyped(self)
|
767
|
-
|
768
|
-
|
769
|
-
class RequiredMultiValueField(BaseField, Generic[ScalarType]):
|
770
|
-
"""
|
771
|
-
Required Multi Value Field.
|
772
|
-
|
773
|
-
A decorator class providing typed accessors for an underlying Field.
|
774
|
-
Use with required, multi-valued fields.
|
775
|
-
"""
|
776
|
-
|
777
|
-
@dataclass
|
778
|
-
class _RequiredMultiValueFieldType:
|
779
|
-
base_field: RequiredMultiValueField
|
780
|
-
|
781
|
-
@property
|
782
|
-
def value(self) -> List[ScalarType]:
|
783
|
-
"""
|
784
|
-
Typed Value.
|
785
|
-
|
786
|
-
Returns the list of values in the field typed as it's specified in an app manifest.
|
787
|
-
"""
|
788
|
-
typed_values: List[ScalarType] = [
|
789
|
-
cast(ScalarType, self.base_field.scalar_definition.from_str(value=str(field_value)))
|
790
|
-
for field_value in cast(List[str], self.base_field.value)
|
791
|
-
if field_value is not None
|
792
|
-
]
|
793
|
-
return typed_values
|
794
|
-
|
795
|
-
@property
|
796
|
-
def display_value(self) -> str:
|
797
|
-
"""
|
798
|
-
Display Value.
|
799
|
-
|
800
|
-
Return the field's display value as a string.
|
801
|
-
"""
|
802
|
-
# We could try to return display value as List[str] for multi-valued fields, except comma is
|
803
|
-
# an unreliable delimiter since the names within each value can contain it
|
804
|
-
assert self.base_field.display_value is not None
|
805
|
-
return self.base_field.display_value
|
806
|
-
|
807
|
-
@property
|
808
|
-
def typed(self) -> _RequiredMultiValueFieldType:
|
809
|
-
"""
|
810
|
-
Typed.
|
811
|
-
|
812
|
-
Return a reference to a typed field with typesafe accessors.
|
813
|
-
"""
|
814
|
-
return self._RequiredMultiValueFieldType(self)
|
815
|
-
|
816
|
-
|
817
|
-
class OptionalMultiValueField(BaseField, Generic[ScalarType]):
|
818
|
-
"""
|
819
|
-
Optional Multi Value Field.
|
820
|
-
|
821
|
-
A decorator class providing typed accessors for an underlying Field.
|
822
|
-
Use with optional, multi-valued fields.
|
823
|
-
"""
|
824
|
-
|
825
|
-
@dataclass
|
826
|
-
class _OptionalMultiValueFieldType:
|
827
|
-
base_field: OptionalMultiValueField
|
828
|
-
|
829
|
-
@property
|
830
|
-
def value(self) -> Optional[List[ScalarType]]:
|
831
|
-
"""
|
832
|
-
Typed Value.
|
833
|
-
|
834
|
-
Returns the list of values in the field typed as it's specified in an app manifest, if present.
|
835
|
-
"""
|
836
|
-
if self.base_field.value:
|
837
|
-
typed_values: List[ScalarType] = [
|
838
|
-
cast(ScalarType, self.base_field.scalar_definition.from_str(value=str(field_value)))
|
839
|
-
for field_value in cast(List[str], self.base_field.value)
|
840
|
-
if field_value is not None
|
841
|
-
]
|
842
|
-
return typed_values
|
843
|
-
return None
|
844
|
-
|
845
|
-
@property
|
846
|
-
def display_value(self) -> Optional[str]:
|
847
|
-
"""
|
848
|
-
Display Value.
|
849
|
-
|
850
|
-
Return the field's display value as a string, if present.
|
851
|
-
"""
|
852
|
-
# Check to ensure linked before display_value, which will raise NotPresentError on unlinked
|
853
|
-
if self.base_field.value is None:
|
854
|
-
return None
|
855
|
-
return None if self.base_field.display_value == "" else self.base_field.display_value
|
856
|
-
|
857
|
-
@property
|
858
|
-
def typed(self) -> _OptionalMultiValueFieldType:
|
859
|
-
"""
|
860
|
-
Typed.
|
861
|
-
|
862
|
-
Return a reference to a typed field with typesafe accessors.
|
863
|
-
"""
|
864
|
-
return self._OptionalMultiValueFieldType(self)
|
865
|
-
|
866
|
-
|
867
|
-
class RequiredSingleOrMultiValueField(BaseField, Generic[ScalarType]):
|
868
|
-
"""
|
869
|
-
Required Single Or Multi Value Field.
|
870
|
-
|
871
|
-
A decorator class providing typed accessors for an underlying Field.
|
872
|
-
Use with required fields where isMulti is unset.
|
873
|
-
"""
|
874
|
-
|
875
|
-
@dataclass
|
876
|
-
class _RequiredSingleOrMultiValueFieldTyped:
|
877
|
-
base_field: RequiredSingleOrMultiValueField
|
878
|
-
|
879
|
-
@property
|
880
|
-
def value(self) -> Union[ScalarType, List[ScalarType]]:
|
881
|
-
"""
|
882
|
-
Typed Value.
|
883
|
-
|
884
|
-
Returns the value in the field typed as it's specified in an app manifest.
|
885
|
-
"""
|
886
|
-
if isinstance(self.base_field.value, list):
|
887
|
-
typed_values: List[ScalarType] = [
|
888
|
-
cast(ScalarType, self.base_field.scalar_definition.from_str(value=str(field_value)))
|
889
|
-
for field_value in cast(List[str], self.base_field.value)
|
890
|
-
if field_value is not None
|
891
|
-
]
|
892
|
-
return typed_values
|
893
|
-
field_value = str(self.base_field.value)
|
894
|
-
return cast(ScalarType, self.base_field.scalar_definition.from_str(value=field_value))
|
895
|
-
|
896
|
-
@property
|
897
|
-
def display_value(self) -> str:
|
898
|
-
"""
|
899
|
-
Display Value.
|
900
|
-
|
901
|
-
Return the field's display value as a string.
|
902
|
-
"""
|
903
|
-
assert self.base_field.display_value is not None
|
904
|
-
return self.base_field.display_value
|
905
|
-
|
906
|
-
@property
|
907
|
-
def typed(self) -> _RequiredSingleOrMultiValueFieldTyped:
|
908
|
-
"""
|
909
|
-
Typed.
|
910
|
-
|
911
|
-
Return a reference to a typed field with typesafe accessors.
|
912
|
-
"""
|
913
|
-
return self._RequiredSingleOrMultiValueFieldTyped(self)
|
914
|
-
|
915
|
-
|
916
|
-
class OptionalSingleOrMultiValueField(BaseField, Generic[ScalarType]):
|
917
|
-
"""
|
918
|
-
Optional Single Or Multi Value Field.
|
919
|
-
|
920
|
-
A decorator class providing typed accessors for an underlying Field.
|
921
|
-
Use with optional fields where isMulti is unset.
|
922
|
-
"""
|
923
|
-
|
924
|
-
@dataclass
|
925
|
-
class _OptionalSingleOrMultiValueFieldTyped:
|
926
|
-
base_field: OptionalSingleOrMultiValueField
|
927
|
-
|
928
|
-
@property
|
929
|
-
def value(self) -> Optional[Union[ScalarType, List[ScalarType]]]:
|
930
|
-
"""
|
931
|
-
Typed Value.
|
932
|
-
|
933
|
-
Returns the value in the field typed as it's specified in an app manifest, if present.
|
934
|
-
"""
|
935
|
-
if self.base_field.value is not None:
|
936
|
-
if isinstance(self.base_field.value, list):
|
937
|
-
typed_values: List[ScalarType] = [
|
938
|
-
cast(ScalarType, self.base_field.scalar_definition.from_str(value=str(field_value)))
|
939
|
-
for field_value in cast(List[str], self.base_field.value)
|
940
|
-
if field_value is not None
|
941
|
-
]
|
942
|
-
return typed_values
|
943
|
-
else:
|
944
|
-
field_value = str(self.base_field.value)
|
945
|
-
return self.base_field.scalar_definition.from_str(value=field_value)
|
946
|
-
return None
|
947
|
-
|
948
|
-
@property
|
949
|
-
def display_value(self) -> Optional[str]:
|
950
|
-
"""
|
951
|
-
Display Value.
|
952
|
-
|
953
|
-
Return the field's display value as a string, if present.
|
954
|
-
"""
|
955
|
-
# Check to ensure linked before display_value, which will raise NotPresentError on unlinked
|
956
|
-
if self.base_field.value is None:
|
957
|
-
return None
|
958
|
-
return None if self.base_field.display_value == "" else self.base_field.display_value
|
959
|
-
|
960
|
-
@property
|
961
|
-
def typed(self) -> _OptionalSingleOrMultiValueFieldTyped:
|
962
|
-
"""
|
963
|
-
Typed.
|
964
|
-
|
965
|
-
Return a reference to a typed field with typesafe accessors.
|
966
|
-
"""
|
967
|
-
return self._OptionalSingleOrMultiValueFieldTyped(self)
|
968
|
-
|
969
|
-
|
970
|
-
@dataclass
|
971
|
-
class ArrayConfigItem(BaseConfigItem):
|
972
|
-
"""
|
973
|
-
Array Config Item.
|
974
|
-
|
975
|
-
An array config item representing a row.
|
976
|
-
"""
|
977
|
-
|
978
|
-
@property
|
979
|
-
def name(self) -> str:
|
980
|
-
"""Return the user defined name of the array row."""
|
981
|
-
# Config item is not optional for arrays
|
982
|
-
assert self.config_item is not None
|
983
|
-
assert self.config_item.value is not None
|
984
|
-
return cast(str, self.config_item.value)
|
985
|
-
|
986
|
-
def full_path(self) -> List[str]:
|
987
|
-
"""Return the full path of the current array row, inheriting from all parents."""
|
988
|
-
path = super().full_path()
|
989
|
-
return path + [self.name]
|
990
|
-
|
991
|
-
|
992
|
-
@dataclass
|
993
|
-
class ScalarConfigItem(BaseConfigItem):
|
994
|
-
"""
|
995
|
-
Scalar Config Item.
|
996
|
-
|
997
|
-
Scalars are values that can be represented outside the Benchling domain.
|
998
|
-
"""
|
999
|
-
|
1000
|
-
config_item: Optional[ScalarConfigReference]
|
1001
|
-
definition: Optional[ScalarDefinition]
|
1002
|
-
|
1003
|
-
|
1004
|
-
@dataclass
|
1005
|
-
class SecureTextDependency(ScalarConfigItem):
|
1006
|
-
"""
|
1007
|
-
SecureText Config.
|
1008
|
-
|
1009
|
-
A dependency for accessing a secure_text config.
|
1010
|
-
"""
|
1011
|
-
|
1012
|
-
# This is declared Optional because a decryption provider is not required until attempting
|
1013
|
-
# to decrypt a value.
|
1014
|
-
decryption_provider: Optional[BaseDecryptionProvider]
|
1015
|
-
|
1016
|
-
|
1017
|
-
class BaseDependencies:
|
1018
|
-
"""
|
1019
|
-
A base class for implementing dependencies.
|
1020
|
-
|
1021
|
-
Used as a facade for the underlying link store, which holds dependency links configured in Benchling.
|
1022
|
-
"""
|
1023
|
-
|
1024
|
-
_store: DependencyLinkStore
|
1025
|
-
_scalar_definitions: Dict[ScalarConfigItemType, ScalarDefinition]
|
1026
|
-
_unknown_scalar_definition: Optional[ScalarDefinition]
|
1027
|
-
# Will be required at runtime if an app attempts to decrypt a secure_text config
|
1028
|
-
_decryption_provider: Optional[BaseDecryptionProvider]
|
1029
|
-
|
1030
|
-
def __init__(
|
1031
|
-
self,
|
1032
|
-
store: DependencyLinkStore,
|
1033
|
-
scalar_definitions: Dict[ScalarConfigItemType, ScalarDefinition] = DEFAULT_SCALAR_DEFINITIONS,
|
1034
|
-
unknown_scalar_definition: Optional[ScalarDefinition] = None,
|
1035
|
-
decryption_provider: Optional[BaseDecryptionProvider] = None,
|
1036
|
-
):
|
1037
|
-
"""
|
1038
|
-
Initialize Base Dependencies.
|
1039
|
-
|
1040
|
-
:param store: The dependency link store to source dependency links from.
|
1041
|
-
:param scalar_definitions: A map of scalar types from the API definitions to ScalarDefinitions which
|
1042
|
-
determines how we want map them to concrete Python types and values. Can be overridden to customize
|
1043
|
-
deserialization behavior or formatting.
|
1044
|
-
:param unknown_scalar_definition: A scalar definition for handling unknown scalar types from the API. Can be
|
1045
|
-
used to control behavior for forwards compatibility with new types the SDK does not yet support (e.g.,
|
1046
|
-
by treating them as strings).
|
1047
|
-
:param decryption_provider: A decryption provider that can decrypt secrets from app config. If
|
1048
|
-
dependencies attempt to use a secure_text's decrypted value, a decryption_provider must be specified.
|
1049
|
-
"""
|
1050
|
-
self._store = store
|
1051
|
-
self._scalar_definitions = scalar_definitions
|
1052
|
-
self._unknown_scalar_definition = unknown_scalar_definition
|
1053
|
-
self._decryption_provider = decryption_provider
|
1054
|
-
|
1055
|
-
@classmethod
|
1056
|
-
def from_app(
|
1057
|
-
cls: Type[D],
|
1058
|
-
client: Benchling,
|
1059
|
-
app_id: str,
|
1060
|
-
decryption_provider: Optional[BaseDecryptionProvider] = None,
|
1061
|
-
) -> D:
|
1062
|
-
"""Initialize dependencies from an app_id."""
|
1063
|
-
link_store = DependencyLinkStore.from_app(client=client, app_id=app_id)
|
1064
|
-
return cls(link_store, decryption_provider=decryption_provider)
|
1065
|
-
|
1066
|
-
@classmethod
|
1067
|
-
def from_store(
|
1068
|
-
cls: Type[D],
|
1069
|
-
store: DependencyLinkStore,
|
1070
|
-
decryption_provider: Optional[BaseDecryptionProvider] = None,
|
1071
|
-
) -> D:
|
1072
|
-
"""Initialize dependencies from a store."""
|
1073
|
-
return cls(store=store, decryption_provider=decryption_provider)
|
1074
|
-
|
1075
|
-
def invalidate_cache(self) -> None:
|
1076
|
-
"""Invalidate the cache of dependency links and force an update."""
|
1077
|
-
self._store.invalidate_cache()
|
1078
|
-
|
1079
|
-
|
1080
|
-
def _supported_config_item(config_item: AppConfigItem) -> ConfigurationReference:
|
1081
|
-
if isinstance(config_item, UnknownType):
|
1082
|
-
raise UnsupportedDependencyError(
|
1083
|
-
f"Unable to read app configuration with unsupported type: {config_item}"
|
1084
|
-
)
|
1085
|
-
return config_item
|