UncountablePythonSDK 0.0.20__py3-none-any.whl → 0.0.22__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.
Potentially problematic release.
This version of UncountablePythonSDK might be problematic. Click here for more details.
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/METADATA +3 -1
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/RECORD +55 -34
- examples/async_batch.py +36 -0
- examples/upload_files.py +19 -0
- pkgs/type_spec/actions_registry/__main__.py +35 -23
- pkgs/type_spec/actions_registry/emit_typescript.py +71 -9
- pkgs/type_spec/builder.py +125 -8
- pkgs/type_spec/config.py +1 -0
- pkgs/type_spec/emit_open_api.py +197 -16
- pkgs/type_spec/emit_open_api_util.py +18 -0
- pkgs/type_spec/emit_python.py +241 -55
- pkgs/type_spec/load_types.py +48 -5
- pkgs/type_spec/open_api_util.py +13 -33
- pkgs/type_spec/type_info/emit_type_info.py +129 -8
- type_spec/external/api/entity/create_entities.yaml +13 -1
- type_spec/external/api/entity/create_entity.yaml +13 -1
- type_spec/external/api/entity/transition_entity_phase.yaml +44 -0
- type_spec/external/api/permissions/set_core_permissions.yaml +69 -0
- type_spec/external/api/recipes/associate_recipe_as_input.yaml +4 -4
- type_spec/external/api/recipes/create_recipe.yaml +2 -1
- type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +16 -0
- type_spec/external/api/recipes/edit_recipe_inputs.yaml +86 -0
- type_spec/external/api/recipes/get_curve.yaml +4 -1
- type_spec/external/api/recipes/get_recipes_data.yaml +6 -0
- type_spec/external/api/recipes/set_recipe_metadata.yaml +1 -0
- type_spec/external/api/recipes/set_recipe_tags.yaml +62 -0
- uncountable/core/__init__.py +3 -1
- uncountable/core/async_batch.py +22 -0
- uncountable/core/client.py +84 -10
- uncountable/core/file_upload.py +95 -0
- uncountable/core/types.py +22 -0
- uncountable/types/__init__.py +18 -0
- uncountable/types/api/entity/create_entities.py +1 -1
- uncountable/types/api/entity/create_entity.py +1 -1
- uncountable/types/api/entity/transition_entity_phase.py +66 -0
- uncountable/types/api/permissions/__init__.py +1 -0
- uncountable/types/api/permissions/set_core_permissions.py +89 -0
- uncountable/types/api/recipes/associate_recipe_as_input.py +4 -3
- uncountable/types/api/recipes/create_recipe.py +1 -1
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +35 -0
- uncountable/types/api/recipes/edit_recipe_inputs.py +106 -0
- uncountable/types/api/recipes/get_curve.py +2 -1
- uncountable/types/api/recipes/get_recipes_data.py +2 -0
- uncountable/types/api/recipes/set_recipe_tags.py +91 -0
- uncountable/types/async_batch.py +10 -0
- uncountable/types/async_batch_processor.py +154 -0
- uncountable/types/client_base.py +113 -48
- uncountable/types/identifier.py +3 -3
- uncountable/types/permissions.py +46 -0
- uncountable/types/post_base.py +30 -0
- uncountable/types/recipe_inputs.py +30 -0
- uncountable/types/recipe_metadata.py +2 -0
- uncountable/types/recipe_workflow_steps.py +77 -0
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/WHEEL +0 -0
- {UncountablePythonSDK-0.0.20.dist-info → UncountablePythonSDK-0.0.22.dist-info}/top_level.txt +0 -0
uncountable/core/client.py
CHANGED
|
@@ -6,11 +6,16 @@ from enum import StrEnum
|
|
|
6
6
|
from urllib.parse import urljoin
|
|
7
7
|
|
|
8
8
|
import requests
|
|
9
|
+
from requests.exceptions import JSONDecodeError
|
|
9
10
|
|
|
10
11
|
from pkgs.argument_parser import CachedParser
|
|
11
12
|
from pkgs.serialization_util import serialize_for_api
|
|
13
|
+
from pkgs.serialization_util.serialization_helpers import JsonValue
|
|
12
14
|
from uncountable.types.client_base import APIRequest, ClientMethods
|
|
13
15
|
|
|
16
|
+
from .file_upload import FileUpload, FileUploader, UploadedFile
|
|
17
|
+
from .types import AuthDetails, AuthDetailsApiKey
|
|
18
|
+
|
|
14
19
|
DT = typing.TypeVar("DT")
|
|
15
20
|
|
|
16
21
|
|
|
@@ -43,23 +48,77 @@ class HTTPPostRequest(HTTPRequestBase):
|
|
|
43
48
|
HTTPRequest = HTTPPostRequest | HTTPGetRequest
|
|
44
49
|
|
|
45
50
|
|
|
51
|
+
|
|
46
52
|
@dataclass(kw_only=True)
|
|
47
|
-
class
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
class ClientConfig():
|
|
54
|
+
allow_insecure_tls: bool = False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class APIResponseError(BaseException):
|
|
58
|
+
status_code: int
|
|
59
|
+
message: str
|
|
60
|
+
extra_details: dict[str, JsonValue] | None
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self, status_code: int, message: str, extra_details: dict[str, JsonValue] | None
|
|
64
|
+
) -> None:
|
|
65
|
+
super().__init__(status_code, message, extra_details)
|
|
66
|
+
self.status_code = status_code
|
|
67
|
+
self.message = message
|
|
68
|
+
self.extra_details = extra_details
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def construct_error(
|
|
72
|
+
cls, status_code: int, extra_details: dict[str, JsonValue] | None
|
|
73
|
+
) -> "APIResponseError":
|
|
74
|
+
message: str
|
|
75
|
+
match status_code:
|
|
76
|
+
case 403:
|
|
77
|
+
message = "unexpected: unauthorized"
|
|
78
|
+
case 410:
|
|
79
|
+
message = "unexpected: not found"
|
|
80
|
+
case 400:
|
|
81
|
+
message = "unexpected: bad arguments"
|
|
82
|
+
case 501:
|
|
83
|
+
message = "unexpected: unimplemented"
|
|
84
|
+
case 504:
|
|
85
|
+
message = "unexpected: timeout"
|
|
86
|
+
case 404:
|
|
87
|
+
message = "not found"
|
|
88
|
+
case 409:
|
|
89
|
+
message = "bad arguments"
|
|
90
|
+
case 422:
|
|
91
|
+
message = "unprocessable"
|
|
92
|
+
case _:
|
|
93
|
+
message = "unknown error"
|
|
94
|
+
return APIResponseError(
|
|
95
|
+
status_code=status_code, message=message, extra_details=extra_details
|
|
96
|
+
)
|
|
50
97
|
|
|
51
98
|
|
|
52
|
-
|
|
99
|
+
class SDKError(BaseException):
|
|
100
|
+
message: str
|
|
101
|
+
|
|
102
|
+
def __init__(self, message: str) -> None:
|
|
103
|
+
super().__init__(message)
|
|
104
|
+
self.message = message
|
|
105
|
+
|
|
106
|
+
def __str__(self) -> str:
|
|
107
|
+
return f"internal SDK error, please contact Uncountable support: {self.message}"
|
|
53
108
|
|
|
54
109
|
|
|
55
110
|
class Client(ClientMethods):
|
|
56
111
|
_parser_map: dict[type, CachedParser] = {}
|
|
57
112
|
_auth_details: AuthDetails
|
|
58
113
|
_base_url: str
|
|
114
|
+
_file_uploader: FileUploader
|
|
115
|
+
_cfg: ClientConfig
|
|
59
116
|
|
|
60
|
-
def __init__(self, *, base_url: str, auth_details: AuthDetails):
|
|
117
|
+
def __init__(self, *, base_url: str, auth_details: AuthDetails, config: ClientConfig | None = None):
|
|
61
118
|
self._auth_details = auth_details
|
|
62
119
|
self._base_url = base_url
|
|
120
|
+
self._file_uploader = FileUploader(self._base_url, self._auth_details)
|
|
121
|
+
self._cfg = config or ClientConfig()
|
|
63
122
|
|
|
64
123
|
def do_request(self, *, api_request: APIRequest, return_type: type[DT]) -> DT:
|
|
65
124
|
http_request = self._build_http_request(api_request=api_request)
|
|
@@ -69,6 +128,7 @@ class Client(ClientMethods):
|
|
|
69
128
|
http_request.url,
|
|
70
129
|
headers=http_request.headers,
|
|
71
130
|
params=http_request.query_params,
|
|
131
|
+
verify=not self._cfg.allow_insecure_tls
|
|
72
132
|
)
|
|
73
133
|
case HTTPPostRequest():
|
|
74
134
|
response = requests.post(
|
|
@@ -76,19 +136,27 @@ class Client(ClientMethods):
|
|
|
76
136
|
headers=http_request.headers,
|
|
77
137
|
data=http_request.body,
|
|
78
138
|
params=http_request.query_params,
|
|
139
|
+
verify=not self._cfg.allow_insecure_tls
|
|
79
140
|
)
|
|
80
141
|
case _:
|
|
81
142
|
typing.assert_never(http_request)
|
|
82
143
|
if response.status_code < 200 or response.status_code > 299:
|
|
83
|
-
|
|
84
|
-
|
|
144
|
+
extra_details: dict[str, JsonValue] | None = None
|
|
145
|
+
try:
|
|
146
|
+
data = response.json()
|
|
147
|
+
if "error" in data:
|
|
148
|
+
extra_details = data["error"]
|
|
149
|
+
except JSONDecodeError:
|
|
150
|
+
pass
|
|
151
|
+
raise APIResponseError.construct_error(
|
|
152
|
+
status_code=response.status_code, extra_details=extra_details
|
|
153
|
+
)
|
|
85
154
|
cached_parser = self._get_cached_parser(return_type)
|
|
86
155
|
try:
|
|
87
156
|
data = response.json()["data"]
|
|
88
157
|
return cached_parser.parse_api(data)
|
|
89
|
-
except ValueError
|
|
90
|
-
|
|
91
|
-
raise err
|
|
158
|
+
except ValueError | JSONDecodeError:
|
|
159
|
+
raise SDKError("unable to process response")
|
|
92
160
|
|
|
93
161
|
def _get_cached_parser(self, data_type: type[DT]) -> CachedParser[DT]:
|
|
94
162
|
if data_type not in self._parser_map:
|
|
@@ -125,3 +193,9 @@ class Client(ClientMethods):
|
|
|
125
193
|
)
|
|
126
194
|
case _:
|
|
127
195
|
raise ValueError(f"unsupported request method: {method}")
|
|
196
|
+
|
|
197
|
+
def upload_files(
|
|
198
|
+
self: typing.Self, *, file_uploads: list[FileUpload]
|
|
199
|
+
) -> list[UploadedFile]:
|
|
200
|
+
"""Upload files to uncountable, returning file ids that are usable with other SDK operations."""
|
|
201
|
+
return self._file_uploader.upload_files(file_uploads=file_uploads)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Generator, Literal, Self
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
import aiotus
|
|
11
|
+
|
|
12
|
+
from .types import AuthDetails
|
|
13
|
+
|
|
14
|
+
_CHUNK_SIZE = 5 * 1024 * 1024 # s3 requires 5MiB minimum
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FileUploadType(StrEnum):
|
|
18
|
+
MEDIA_FILE_UPLOAD = "MEDIA_FILE_UPLOAD"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(kw_only=True)
|
|
22
|
+
class MediaFileUpload:
|
|
23
|
+
"""Upload file from a path on disk"""
|
|
24
|
+
|
|
25
|
+
path: str
|
|
26
|
+
type: FileUploadType.MEDIA_FILE_UPLOAD = FileUploadType.MEDIA_FILE_UPLOAD
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
FileUpload = MediaFileUpload
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(kw_only=True)
|
|
33
|
+
class FileBytes:
|
|
34
|
+
name: str
|
|
35
|
+
bytes_data: BytesIO
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@contextmanager
|
|
39
|
+
def file_upload_data(file_upload: FileUpload) -> Generator[FileBytes, None, None]:
|
|
40
|
+
match file_upload.type:
|
|
41
|
+
case FileUploadType.MEDIA_FILE_UPLOAD:
|
|
42
|
+
with open(file_upload.path, "rb") as f:
|
|
43
|
+
yield FileBytes(
|
|
44
|
+
name=Path(file_upload.path).name, bytes_data=BytesIO(f.read())
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(kw_only=True)
|
|
49
|
+
class UploadedFile:
|
|
50
|
+
name: str
|
|
51
|
+
file_id: int
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class UploadFailed(Exception):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class FileUploader:
|
|
59
|
+
_auth_details: AuthDetails
|
|
60
|
+
_base_url: str
|
|
61
|
+
|
|
62
|
+
def __init__(self: Self, base_url: str, auth_details: AuthDetails) -> None:
|
|
63
|
+
self._base_url = base_url
|
|
64
|
+
self._auth_details = auth_details
|
|
65
|
+
|
|
66
|
+
async def _upload_file(self: Self, file_upload: FileUpload) -> UploadedFile:
|
|
67
|
+
creation_url = f"{self._base_url}/api/external/file_upload/files"
|
|
68
|
+
auth = aiohttp.BasicAuth(
|
|
69
|
+
self._auth_details.api_id, self._auth_details.api_secret_key
|
|
70
|
+
)
|
|
71
|
+
async with (
|
|
72
|
+
aiohttp.ClientSession(
|
|
73
|
+
auth=auth, headers={"Origin": self._base_url}
|
|
74
|
+
) as session,
|
|
75
|
+
):
|
|
76
|
+
with file_upload_data(file_upload) as file_bytes:
|
|
77
|
+
location = await aiotus.upload(
|
|
78
|
+
creation_url,
|
|
79
|
+
file_bytes.bytes_data,
|
|
80
|
+
{"filename": file_bytes.name.encode()},
|
|
81
|
+
client_session=session,
|
|
82
|
+
chunksize=_CHUNK_SIZE,
|
|
83
|
+
)
|
|
84
|
+
if location is None:
|
|
85
|
+
raise UploadFailed(f"Failed to upload: {file_bytes.name}")
|
|
86
|
+
return UploadedFile(
|
|
87
|
+
name=file_bytes.name, file_id=int(location.path.split("/")[-1])
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def upload_files(
|
|
91
|
+
self: Self, *, file_uploads: list[FileUpload]
|
|
92
|
+
) -> list[UploadedFile]:
|
|
93
|
+
return [
|
|
94
|
+
asyncio.run(self._upload_file(file_upload)) for file_upload in file_uploads
|
|
95
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import typing
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
from urllib.parse import urljoin
|
|
7
|
+
|
|
8
|
+
import aiohttp
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from pkgs.argument_parser import CachedParser
|
|
12
|
+
from pkgs.serialization_util import serialize_for_api
|
|
13
|
+
from uncountable.types.client_base import APIRequest, ClientMethods
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(kw_only=True)
|
|
17
|
+
class AuthDetailsApiKey:
|
|
18
|
+
api_id: str
|
|
19
|
+
api_secret_key: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
AuthDetails = AuthDetailsApiKey
|
uncountable/types/__init__.py
CHANGED
|
@@ -17,6 +17,8 @@ from .api.recipes import create_recipe as create_recipe_t
|
|
|
17
17
|
from .api.recipe_links import create_recipe_link as create_recipe_link_t
|
|
18
18
|
from .api.recipes import create_recipes as create_recipes_t
|
|
19
19
|
from . import curves as curves_t
|
|
20
|
+
from .api.recipes import disassociate_recipe_as_input as disassociate_recipe_as_input_t
|
|
21
|
+
from .api.recipes import edit_recipe_inputs as edit_recipe_inputs_t
|
|
20
22
|
from . import entity as entity_t
|
|
21
23
|
from .api.batch import execute_batch as execute_batch_t
|
|
22
24
|
from .api.batch import execute_batch_load_async as execute_batch_load_async_t
|
|
@@ -47,21 +49,28 @@ from .api.entity import list_entities as list_entities_t
|
|
|
47
49
|
from .api.id_source import list_id_source as list_id_source_t
|
|
48
50
|
from .api.id_source import match_id_source as match_id_source_t
|
|
49
51
|
from . import outputs as outputs_t
|
|
52
|
+
from . import permissions as permissions_t
|
|
50
53
|
from . import phases as phases_t
|
|
54
|
+
from . import post_base as post_base_t
|
|
51
55
|
from . import recipe_identifiers as recipe_identifiers_t
|
|
56
|
+
from . import recipe_inputs as recipe_inputs_t
|
|
52
57
|
from . import recipe_links as recipe_links_t
|
|
53
58
|
from . import recipe_metadata as recipe_metadata_t
|
|
54
59
|
from . import recipe_output_metadata as recipe_output_metadata_t
|
|
55
60
|
from . import recipe_tags as recipe_tags_t
|
|
61
|
+
from . import recipe_workflow_steps as recipe_workflow_steps_t
|
|
56
62
|
from .api.entity import resolve_entity_ids as resolve_entity_ids_t
|
|
57
63
|
from .api.outputs import resolve_output_conditions as resolve_output_conditions_t
|
|
58
64
|
from . import response as response_t
|
|
59
65
|
from .api.triggers import run_trigger as run_trigger_t
|
|
66
|
+
from .api.permissions import set_core_permissions as set_core_permissions_t
|
|
60
67
|
from .api.inputs import set_input_attribute_values as set_input_attribute_values_t
|
|
61
68
|
from .api.recipes import set_recipe_inputs as set_recipe_inputs_t
|
|
62
69
|
from .api.recipes import set_recipe_metadata as set_recipe_metadata_t
|
|
63
70
|
from .api.recipes import set_recipe_outputs as set_recipe_outputs_t
|
|
71
|
+
from .api.recipes import set_recipe_tags as set_recipe_tags_t
|
|
64
72
|
from .api.entity import set_values as set_values_t
|
|
73
|
+
from .api.entity import transition_entity_phase as transition_entity_phase_t
|
|
65
74
|
from . import units as units_t
|
|
66
75
|
from . import users as users_t
|
|
67
76
|
from . import workflows as workflows_t
|
|
@@ -82,6 +91,8 @@ __all__: list[str] = [
|
|
|
82
91
|
"create_recipe_link_t",
|
|
83
92
|
"create_recipes_t",
|
|
84
93
|
"curves_t",
|
|
94
|
+
"disassociate_recipe_as_input_t",
|
|
95
|
+
"edit_recipe_inputs_t",
|
|
85
96
|
"entity_t",
|
|
86
97
|
"execute_batch_t",
|
|
87
98
|
"execute_batch_load_async_t",
|
|
@@ -112,21 +123,28 @@ __all__: list[str] = [
|
|
|
112
123
|
"list_id_source_t",
|
|
113
124
|
"match_id_source_t",
|
|
114
125
|
"outputs_t",
|
|
126
|
+
"permissions_t",
|
|
115
127
|
"phases_t",
|
|
128
|
+
"post_base_t",
|
|
116
129
|
"recipe_identifiers_t",
|
|
130
|
+
"recipe_inputs_t",
|
|
117
131
|
"recipe_links_t",
|
|
118
132
|
"recipe_metadata_t",
|
|
119
133
|
"recipe_output_metadata_t",
|
|
120
134
|
"recipe_tags_t",
|
|
135
|
+
"recipe_workflow_steps_t",
|
|
121
136
|
"resolve_entity_ids_t",
|
|
122
137
|
"resolve_output_conditions_t",
|
|
123
138
|
"response_t",
|
|
124
139
|
"run_trigger_t",
|
|
140
|
+
"set_core_permissions_t",
|
|
125
141
|
"set_input_attribute_values_t",
|
|
126
142
|
"set_recipe_inputs_t",
|
|
127
143
|
"set_recipe_metadata_t",
|
|
128
144
|
"set_recipe_outputs_t",
|
|
145
|
+
"set_recipe_tags_t",
|
|
129
146
|
"set_values_t",
|
|
147
|
+
"transition_entity_phase_t",
|
|
130
148
|
"units_t",
|
|
131
149
|
"users_t",
|
|
132
150
|
"workflows_t",
|
|
@@ -34,7 +34,7 @@ class EntityToCreate:
|
|
|
34
34
|
@dataclass(kw_only=True)
|
|
35
35
|
class Arguments:
|
|
36
36
|
definition_id: base_t.ObjectId
|
|
37
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS]]
|
|
37
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK]]
|
|
38
38
|
entities_to_create: list[EntityToCreate]
|
|
39
39
|
|
|
40
40
|
|
|
@@ -40,7 +40,7 @@ class EntityFieldInitialValue:
|
|
|
40
40
|
@dataclass(kw_only=True)
|
|
41
41
|
class Arguments:
|
|
42
42
|
definition_id: base_t.ObjectId
|
|
43
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS]]
|
|
43
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK]]
|
|
44
44
|
field_values: typing.Optional[typing.Optional[list[field_values_t.FieldRefNameValue]]] = None
|
|
45
45
|
|
|
46
46
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
2
|
+
# flake8: noqa: F821
|
|
3
|
+
# ruff: noqa: E402
|
|
4
|
+
# fmt: off
|
|
5
|
+
# isort: skip_file
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import typing # noqa: F401
|
|
8
|
+
import datetime # noqa: F401
|
|
9
|
+
from decimal import Decimal # noqa: F401
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pkgs.serialization import serial_class
|
|
12
|
+
from ... import entity as entity_t
|
|
13
|
+
from ... import identifier as identifier_t
|
|
14
|
+
from ... import response as response_t
|
|
15
|
+
|
|
16
|
+
__all__: list[str] = [
|
|
17
|
+
"Arguments",
|
|
18
|
+
"Data",
|
|
19
|
+
"ENDPOINT_METHOD",
|
|
20
|
+
"ENDPOINT_PATH",
|
|
21
|
+
"TransitionIdentifier",
|
|
22
|
+
"TransitionIdentifierPhases",
|
|
23
|
+
"TransitionIdentifierTransition",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
ENDPOINT_METHOD = "POST"
|
|
27
|
+
ENDPOINT_PATH = "api/external/entity/transition_entity_phase"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
31
|
+
@serial_class(
|
|
32
|
+
parse_require={"type"},
|
|
33
|
+
)
|
|
34
|
+
@dataclass(kw_only=True)
|
|
35
|
+
class TransitionIdentifierPhases:
|
|
36
|
+
type: typing.Literal["phases"] = "phases"
|
|
37
|
+
phase_from_key: identifier_t.IdentifierKey
|
|
38
|
+
phase_to_key: identifier_t.IdentifierKey
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
42
|
+
@serial_class(
|
|
43
|
+
parse_require={"type"},
|
|
44
|
+
)
|
|
45
|
+
@dataclass(kw_only=True)
|
|
46
|
+
class TransitionIdentifierTransition:
|
|
47
|
+
type: typing.Literal["transition"] = "transition"
|
|
48
|
+
transition_key: identifier_t.IdentifierKey
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
52
|
+
TransitionIdentifier = typing.Union[TransitionIdentifierPhases, TransitionIdentifierTransition]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
56
|
+
@dataclass(kw_only=True)
|
|
57
|
+
class Arguments:
|
|
58
|
+
entity: entity_t.Entity
|
|
59
|
+
transition: TransitionIdentifier
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
63
|
+
@dataclass(kw_only=True)
|
|
64
|
+
class Data(response_t.Response):
|
|
65
|
+
pass
|
|
66
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
2
|
+
# flake8: noqa: F821
|
|
3
|
+
# ruff: noqa: E402
|
|
4
|
+
# fmt: off
|
|
5
|
+
# isort: skip_file
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import typing # noqa: F401
|
|
8
|
+
import datetime # noqa: F401
|
|
9
|
+
from decimal import Decimal # noqa: F401
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pkgs.serialization import serial_class
|
|
12
|
+
from ... import identifier as identifier_t
|
|
13
|
+
from ... import permissions as permissions_t
|
|
14
|
+
from ... import post_base as post_base_t
|
|
15
|
+
|
|
16
|
+
__all__: list[str] = [
|
|
17
|
+
"Arguments",
|
|
18
|
+
"Data",
|
|
19
|
+
"ENDPOINT_METHOD",
|
|
20
|
+
"ENDPOINT_PATH",
|
|
21
|
+
"PermissionsScope",
|
|
22
|
+
"PermissionsScopeAllMaterialFamilies",
|
|
23
|
+
"PermissionsScopeMaterialFamily",
|
|
24
|
+
"PermissionsScopeProject",
|
|
25
|
+
"PermissionsScopeRecipe",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
ENDPOINT_METHOD = "POST"
|
|
29
|
+
ENDPOINT_PATH = "api/external/permissions/external_set_core_permissions"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
33
|
+
@serial_class(
|
|
34
|
+
parse_require={"type"},
|
|
35
|
+
)
|
|
36
|
+
@dataclass(kw_only=True)
|
|
37
|
+
class PermissionsScopeProject:
|
|
38
|
+
type: typing.Literal["project"] = "project"
|
|
39
|
+
project_key: identifier_t.IdentifierKey
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
43
|
+
@serial_class(
|
|
44
|
+
parse_require={"type"},
|
|
45
|
+
)
|
|
46
|
+
@dataclass(kw_only=True)
|
|
47
|
+
class PermissionsScopeRecipe:
|
|
48
|
+
type: typing.Literal["recipe"] = "recipe"
|
|
49
|
+
recipe_key: identifier_t.IdentifierKey
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
53
|
+
@serial_class(
|
|
54
|
+
parse_require={"type"},
|
|
55
|
+
)
|
|
56
|
+
@dataclass(kw_only=True)
|
|
57
|
+
class PermissionsScopeMaterialFamily:
|
|
58
|
+
type: typing.Literal["material_family"] = "material_family"
|
|
59
|
+
material_family_key: identifier_t.IdentifierKey
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
63
|
+
@serial_class(
|
|
64
|
+
parse_require={"type"},
|
|
65
|
+
)
|
|
66
|
+
@dataclass(kw_only=True)
|
|
67
|
+
class PermissionsScopeAllMaterialFamilies:
|
|
68
|
+
type: typing.Literal["all_material_families"] = "all_material_families"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
72
|
+
PermissionsScope = typing.Union[PermissionsScopeProject, PermissionsScopeRecipe, PermissionsScopeMaterialFamily, PermissionsScopeAllMaterialFamilies]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
76
|
+
@dataclass(kw_only=True)
|
|
77
|
+
class Arguments:
|
|
78
|
+
scope: PermissionsScope
|
|
79
|
+
permissions_types: list[permissions_t.CorePermissionType]
|
|
80
|
+
update_type: post_base_t.UpdateType
|
|
81
|
+
user_group_ids: typing.Optional[list[int]] = None
|
|
82
|
+
user_ids: typing.Optional[list[int]] = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
86
|
+
@dataclass(kw_only=True)
|
|
87
|
+
class Data:
|
|
88
|
+
pass
|
|
89
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -8,7 +8,7 @@ import typing # noqa: F401
|
|
|
8
8
|
import datetime # noqa: F401
|
|
9
9
|
from decimal import Decimal # noqa: F401
|
|
10
10
|
from dataclasses import dataclass
|
|
11
|
-
from ... import
|
|
11
|
+
from ... import async_batch as async_batch_t
|
|
12
12
|
from ... import identifier as identifier_t
|
|
13
13
|
|
|
14
14
|
__all__: list[str] = [
|
|
@@ -26,10 +26,11 @@ ENDPOINT_PATH = "api/external/recipes/associate_recipe_as_input"
|
|
|
26
26
|
@dataclass(kw_only=True)
|
|
27
27
|
class Arguments:
|
|
28
28
|
recipe_key: identifier_t.IdentifierKey
|
|
29
|
+
input_key: typing.Optional[identifier_t.IdentifierKey] = None
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
# DO NOT MODIFY -- This file is generated by type_spec
|
|
32
33
|
@dataclass(kw_only=True)
|
|
33
|
-
class Data:
|
|
34
|
-
|
|
34
|
+
class Data(async_batch_t.AsyncBatchActionReturn):
|
|
35
|
+
pass
|
|
35
36
|
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -29,10 +29,10 @@ ENDPOINT_PATH = "api/external/recipes/create_recipe"
|
|
|
29
29
|
class Arguments:
|
|
30
30
|
material_family_id: base_t.ObjectId
|
|
31
31
|
workflow_id: base_t.ObjectId
|
|
32
|
-
identifiers: recipe_identifiers_t.RecipeIdentifiers
|
|
33
32
|
name: typing.Optional[str] = None
|
|
34
33
|
workflow_variant_id: typing.Optional[typing.Optional[base_t.ObjectId]] = None
|
|
35
34
|
recipe_metadata: typing.Optional[list[recipe_metadata_t.MetadataValue]] = None
|
|
35
|
+
identifiers: typing.Optional[recipe_identifiers_t.RecipeIdentifiers] = None
|
|
36
36
|
definition_key: typing.Optional[identifier_t.IdentifierKey] = None
|
|
37
37
|
|
|
38
38
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
2
|
+
# flake8: noqa: F821
|
|
3
|
+
# ruff: noqa: E402
|
|
4
|
+
# fmt: off
|
|
5
|
+
# isort: skip_file
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import typing # noqa: F401
|
|
8
|
+
import datetime # noqa: F401
|
|
9
|
+
from decimal import Decimal # noqa: F401
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from ... import async_batch as async_batch_t
|
|
12
|
+
from ... import identifier as identifier_t
|
|
13
|
+
|
|
14
|
+
__all__: list[str] = [
|
|
15
|
+
"Arguments",
|
|
16
|
+
"Data",
|
|
17
|
+
"ENDPOINT_METHOD",
|
|
18
|
+
"ENDPOINT_PATH",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
ENDPOINT_METHOD = "POST"
|
|
22
|
+
ENDPOINT_PATH = "api/external/recipes/disassociate_recipe_as_input"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
26
|
+
@dataclass(kw_only=True)
|
|
27
|
+
class Arguments:
|
|
28
|
+
recipe_key: identifier_t.IdentifierKey
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
32
|
+
@dataclass(kw_only=True)
|
|
33
|
+
class Data(async_batch_t.AsyncBatchActionReturn):
|
|
34
|
+
pass
|
|
35
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|