wandelbots-api-client 25.8.0.dev55__py3-none-any.whl → 25.8.0.dev56__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.
- wandelbots_api_client/__init__.py +1 -1
- wandelbots_api_client/api_client.py +1 -1
- wandelbots_api_client/authorization.py +244 -0
- wandelbots_api_client/configuration.py +1 -1
- wandelbots_api_client/v2/__init__.py +1 -1
- wandelbots_api_client/v2/api_client.py +1 -1
- wandelbots_api_client/v2/configuration.py +1 -1
- {wandelbots_api_client-25.8.0.dev55.dist-info → wandelbots_api_client-25.8.0.dev56.dist-info}/METADATA +2 -2
- {wandelbots_api_client-25.8.0.dev55.dist-info → wandelbots_api_client-25.8.0.dev56.dist-info}/RECORD +12 -11
- {wandelbots_api_client-25.8.0.dev55.dist-info → wandelbots_api_client-25.8.0.dev56.dist-info}/WHEEL +0 -0
- {wandelbots_api_client-25.8.0.dev55.dist-info → wandelbots_api_client-25.8.0.dev56.dist-info}/licenses/LICENSE +0 -0
- {wandelbots_api_client-25.8.0.dev55.dist-info → wandelbots_api_client-25.8.0.dev56.dist-info}/top_level.txt +0 -0
|
@@ -90,7 +90,7 @@ class ApiClient:
|
|
|
90
90
|
self.default_headers[header_name] = header_value
|
|
91
91
|
self.cookie = cookie
|
|
92
92
|
# Set default User-Agent.
|
|
93
|
-
self.user_agent = 'Wandelbots-Nova-API-Python-Client/25.8.0.
|
|
93
|
+
self.user_agent = 'Wandelbots-Nova-API-Python-Client/25.8.0.dev56'
|
|
94
94
|
self.client_side_validation = configuration.client_side_validation
|
|
95
95
|
|
|
96
96
|
async def __aenter__(self):
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import aiohttp
|
|
5
|
+
import pydantic
|
|
6
|
+
|
|
7
|
+
class Auth0Config(pydantic.BaseModel):
|
|
8
|
+
"""Configuration for Auth0 authentication"""
|
|
9
|
+
|
|
10
|
+
domain: Optional[str] = None
|
|
11
|
+
client_id: Optional[str] = None
|
|
12
|
+
audience: Optional[str] = None
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def default(cls) -> "Auth0Config":
|
|
16
|
+
"""Create Auth0Config from environment variables"""
|
|
17
|
+
return cls(
|
|
18
|
+
domain="auth.portal.wandelbots.io",
|
|
19
|
+
client_id="J7WJUi38xVQdJAEBNRT9Xw1b0fXDb4J2",
|
|
20
|
+
audience="nova-api",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def is_complete(self) -> bool:
|
|
24
|
+
"""Check if all required fields are set and not None"""
|
|
25
|
+
return bool(self.domain and self.client_id and self.audience)
|
|
26
|
+
|
|
27
|
+
def get_validated_config(self) -> tuple[str, str, str]:
|
|
28
|
+
"""Get validated config values, ensuring they are not None"""
|
|
29
|
+
if not self.is_complete():
|
|
30
|
+
raise ValueError("Auth0 configuration is incomplete")
|
|
31
|
+
return self.domain, self.client_id, self.audience
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Auth0DeviceCodeInfo(pydantic.BaseModel):
|
|
35
|
+
"""
|
|
36
|
+
Model to store device code information.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
device_code (str): The device code.
|
|
40
|
+
user_code (str): The user code.
|
|
41
|
+
verification_uri (str): The verification URI.
|
|
42
|
+
expires_in (int): The expiration time in seconds.
|
|
43
|
+
interval (int): The interval time in seconds (default is 5).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
device_code: str
|
|
47
|
+
user_code: str
|
|
48
|
+
verification_uri: str
|
|
49
|
+
expires_in: int
|
|
50
|
+
interval: int = pydantic.Field(default=5)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Auth0TokenInfo(pydantic.BaseModel):
|
|
54
|
+
"""
|
|
55
|
+
Model to store token information.
|
|
56
|
+
|
|
57
|
+
Attributes:
|
|
58
|
+
access_token (str): The access token.
|
|
59
|
+
refresh_token (str, optional): The refresh token.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
access_token: str
|
|
63
|
+
refresh_token: Optional[str] = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Auth0DeviceAuthorization:
|
|
67
|
+
"""
|
|
68
|
+
Class to handle Auth0 device authorization.
|
|
69
|
+
|
|
70
|
+
Methods:
|
|
71
|
+
__init__(auth0_config: Auth0Config | None = None):
|
|
72
|
+
Initializes the Auth0DeviceAuthorization instance with the given parameters.
|
|
73
|
+
request_device_code():
|
|
74
|
+
Requests a device code from Auth0.
|
|
75
|
+
display_user_instructions():
|
|
76
|
+
Displays instructions for the user to authenticate.
|
|
77
|
+
poll_token_endpoint():
|
|
78
|
+
Polls the token endpoint to obtain an access token.
|
|
79
|
+
refresh_access_token(refresh_token: str):
|
|
80
|
+
Refreshes the access token using the refresh token.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, auth0_config: Optional[Auth0Config] = None):
|
|
84
|
+
"""
|
|
85
|
+
Initialize with Auth0Config from env vars or passed config.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
auth0_config: Optional Auth0Config object. If not provided,
|
|
89
|
+
will be created using default configuration.
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
self.params = auth0_config or Auth0Config.default()
|
|
93
|
+
if not self.params.is_complete():
|
|
94
|
+
raise ValueError("Auth0 configuration is incomplete")
|
|
95
|
+
|
|
96
|
+
domain, client_id, audience = self.params.get_validated_config()
|
|
97
|
+
self.auth0_domain = domain
|
|
98
|
+
self.auth0_client_id = client_id
|
|
99
|
+
self.auth0_audience = audience
|
|
100
|
+
|
|
101
|
+
except ValueError as e:
|
|
102
|
+
raise ValueError(f"Error: Auth0 configuration is invalid: {e}")
|
|
103
|
+
|
|
104
|
+
self.headers = {"content-type": "application/x-www-form-urlencoded"}
|
|
105
|
+
self.device_code_info: Optional[Auth0DeviceCodeInfo] = None
|
|
106
|
+
self.refresh_token: Optional[str] = None
|
|
107
|
+
self.interval = 5
|
|
108
|
+
self.attempts = 10
|
|
109
|
+
|
|
110
|
+
async def request_device_code(self) -> Auth0DeviceCodeInfo:
|
|
111
|
+
"""
|
|
112
|
+
Requests a device code from Auth0.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Auth0DeviceCodeInfo: The device code information.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
Exception: If there is an error requesting the device code.
|
|
119
|
+
"""
|
|
120
|
+
device_code_url = f"https://{self.auth0_domain}/oauth/device/code"
|
|
121
|
+
data = {
|
|
122
|
+
"client_id": self.auth0_client_id,
|
|
123
|
+
"scope": "openid profile email",
|
|
124
|
+
"audience": self.auth0_audience,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async with aiohttp.ClientSession() as session:
|
|
128
|
+
async with session.post(device_code_url, headers=self.headers, data=data) as response:
|
|
129
|
+
if response.status == 200:
|
|
130
|
+
response_data = await response.json()
|
|
131
|
+
self.device_code_info = Auth0DeviceCodeInfo(**response_data)
|
|
132
|
+
self.interval = self.device_code_info.interval
|
|
133
|
+
return self.device_code_info
|
|
134
|
+
else:
|
|
135
|
+
error_data = await response.json()
|
|
136
|
+
raise Exception("Error requesting device code:", error_data)
|
|
137
|
+
|
|
138
|
+
def get_device_code_info(self) -> Optional[Auth0DeviceCodeInfo]:
|
|
139
|
+
"""
|
|
140
|
+
Returns the device code information.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Optional[Auth0DeviceCodeInfo]: The device code information.
|
|
144
|
+
"""
|
|
145
|
+
return self.device_code_info
|
|
146
|
+
|
|
147
|
+
def display_user_instructions(self) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Displays instructions for the user to authenticate.
|
|
150
|
+
|
|
151
|
+
Raises:
|
|
152
|
+
Exception: If device code information is not available.
|
|
153
|
+
"""
|
|
154
|
+
if self.device_code_info:
|
|
155
|
+
verification_uri = f"{self.device_code_info.verification_uri}?user_code={self.device_code_info.user_code}"
|
|
156
|
+
user_code = self.device_code_info.user_code
|
|
157
|
+
print(
|
|
158
|
+
f"Please visit {verification_uri} and validate the code {user_code} to authenticate."
|
|
159
|
+
)
|
|
160
|
+
else:
|
|
161
|
+
raise Exception("Device code information is not available.")
|
|
162
|
+
|
|
163
|
+
async def poll_token_endpoint(self):
|
|
164
|
+
"""
|
|
165
|
+
Polls the token endpoint to obtain an access token.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
str: The access token.
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
Exception: If there is an error polling the token endpoint.
|
|
172
|
+
"""
|
|
173
|
+
if not self.device_code_info:
|
|
174
|
+
raise Exception("Device code information is not available.")
|
|
175
|
+
|
|
176
|
+
token_url = f"https://{self.auth0_domain}/oauth/token"
|
|
177
|
+
token_data = {
|
|
178
|
+
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
|
|
179
|
+
"device_code": self.device_code_info.device_code,
|
|
180
|
+
"client_id": self.auth0_client_id,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async with aiohttp.ClientSession() as session:
|
|
184
|
+
while self.attempts > 0:
|
|
185
|
+
self.attempts = self.attempts - 1
|
|
186
|
+
async with session.post(token_url, headers=self.headers, data=token_data) as token_response:
|
|
187
|
+
if token_response.status == 200:
|
|
188
|
+
# If the response status is 200, it means the access token is successfully obtained.
|
|
189
|
+
response_data = await token_response.json()
|
|
190
|
+
token_info = Auth0TokenInfo(**response_data)
|
|
191
|
+
self.refresh_token = token_info.refresh_token
|
|
192
|
+
return token_info.access_token
|
|
193
|
+
elif token_response.status == 400:
|
|
194
|
+
# If the response status is 400, check the error type.
|
|
195
|
+
error_data = await token_response.json()
|
|
196
|
+
error = error_data.get("error")
|
|
197
|
+
if error == "authorization_pending":
|
|
198
|
+
# If the error is 'authorization_pending', it means the user has not yet authorized.
|
|
199
|
+
await asyncio.sleep(self.interval)
|
|
200
|
+
elif error == "slow_down":
|
|
201
|
+
# If the error is 'slow_down', it means the server requests to slow down polling.
|
|
202
|
+
self.interval += 5
|
|
203
|
+
await asyncio.sleep(self.interval)
|
|
204
|
+
elif token_response.status == 403:
|
|
205
|
+
# If the response status is 403, it means the request is forbidden, wait and retry.
|
|
206
|
+
await asyncio.sleep(self.interval)
|
|
207
|
+
else:
|
|
208
|
+
# For other status codes, raise an exception with the error details.
|
|
209
|
+
error_data = await token_response.json()
|
|
210
|
+
raise Exception("Error:", token_response.status, error_data)
|
|
211
|
+
raise Exception("Error: It was not able to authenticate. Please try again.")
|
|
212
|
+
|
|
213
|
+
async def refresh_access_token(self, refresh_token: str):
|
|
214
|
+
"""
|
|
215
|
+
Refreshes the access token using the refresh token.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
refresh_token (str): The refresh token.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
str: The new access token.
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
Exception: If there is an error refreshing the access token.
|
|
225
|
+
"""
|
|
226
|
+
if not refresh_token:
|
|
227
|
+
raise Exception("Refresh token is not available.")
|
|
228
|
+
|
|
229
|
+
token_url = f"https://{self.auth0_domain}/oauth/token"
|
|
230
|
+
token_data = {
|
|
231
|
+
"grant_type": "refresh_token",
|
|
232
|
+
"client_id": self.auth0_client_id,
|
|
233
|
+
"refresh_token": refresh_token,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async with aiohttp.ClientSession() as session:
|
|
237
|
+
async with session.post(token_url, headers=self.headers, data=token_data) as response:
|
|
238
|
+
if response.status == 200:
|
|
239
|
+
response_data = await response.json()
|
|
240
|
+
token_info = Auth0TokenInfo(**response_data)
|
|
241
|
+
return token_info.access_token
|
|
242
|
+
else:
|
|
243
|
+
error_data = await response.json()
|
|
244
|
+
raise Exception("Error refreshing access token:", error_data)
|
|
@@ -524,7 +524,7 @@ conf = wandelbots_api_client.Configuration(
|
|
|
524
524
|
"OS: {env}\n"\
|
|
525
525
|
"Python Version: {pyversion}\n"\
|
|
526
526
|
"Version of the API: 1.0.0\n"\
|
|
527
|
-
"SDK Package Version: 25.8.0.
|
|
527
|
+
"SDK Package Version: 25.8.0.dev56".\
|
|
528
528
|
format(env=sys.platform, pyversion=sys.version)
|
|
529
529
|
|
|
530
530
|
def get_host_settings(self) -> List[HostSetting]:
|
|
@@ -90,7 +90,7 @@ class ApiClient:
|
|
|
90
90
|
self.default_headers[header_name] = header_value
|
|
91
91
|
self.cookie = cookie
|
|
92
92
|
# Set default User-Agent.
|
|
93
|
-
self.user_agent = 'Wandelbots-Nova-API-Python-Client/25.8.0.
|
|
93
|
+
self.user_agent = 'Wandelbots-Nova-API-Python-Client/25.8.0.dev56'
|
|
94
94
|
self.client_side_validation = configuration.client_side_validation
|
|
95
95
|
|
|
96
96
|
async def __aenter__(self):
|
|
@@ -524,7 +524,7 @@ conf = wandelbots_api_client.v2.Configuration(
|
|
|
524
524
|
"OS: {env}\n"\
|
|
525
525
|
"Python Version: {pyversion}\n"\
|
|
526
526
|
"Version of the API: 2.0.0 beta\n"\
|
|
527
|
-
"SDK Package Version: 25.8.0.
|
|
527
|
+
"SDK Package Version: 25.8.0.dev56".\
|
|
528
528
|
format(env=sys.platform, pyversion=sys.version)
|
|
529
529
|
|
|
530
530
|
def get_host_settings(self) -> List[HostSetting]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wandelbots_api_client
|
|
3
|
-
Version: 25.8.0.
|
|
3
|
+
Version: 25.8.0.dev56
|
|
4
4
|
Summary: Wandelbots Python Client: Interact with robots in an easy and intuitive way.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author: Copyright (c) 2025 Wandelbots GmbH
|
|
@@ -34,7 +34,7 @@ Dynamic: license-file
|
|
|
34
34
|
Interact with robots in an easy and intuitive way.
|
|
35
35
|
|
|
36
36
|
- Compatible API version: 1.0.0 (can be found at the home screen of your instance -> API)
|
|
37
|
-
- Package version: 25.8.0.
|
|
37
|
+
- Package version: 25.8.0.dev56
|
|
38
38
|
|
|
39
39
|
Sign up for our [Wandelbots Developers Beta program](https://www.wandelbots.com/developers-beta) to get access to the Wandelbots API.
|
|
40
40
|
|
{wandelbots_api_client-25.8.0.dev55.dist-info → wandelbots_api_client-25.8.0.dev56.dist-info}/RECORD
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
wandelbots_api_client/__init__.py,sha256=
|
|
2
|
-
wandelbots_api_client/api_client.py,sha256=
|
|
1
|
+
wandelbots_api_client/__init__.py,sha256=MnULAzt4nTj0m4gteIHwBz9xgmoB5eoZspyKQ1aC7FM,1023
|
|
2
|
+
wandelbots_api_client/api_client.py,sha256=MKIGav9zGRDyy-GFRynt35iYw2kdEqeIvLp6_OgUCO0,27596
|
|
3
3
|
wandelbots_api_client/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
|
|
4
|
-
wandelbots_api_client/
|
|
4
|
+
wandelbots_api_client/authorization.py,sha256=DyGsVHmD-1D4C9rMEEtQLi5YvbxPxgt7MqKVlwQgC3o,9499
|
|
5
|
+
wandelbots_api_client/configuration.py,sha256=2LzyoZdVxLtxLEPcXgoA5Mv0wPhUr9EDb_IMDkEjuE8,18313
|
|
5
6
|
wandelbots_api_client/exceptions.py,sha256=4ukZiylEwTUCen4G_zzHViQOC0Desk52mRekPGrRcKQ,6428
|
|
6
7
|
wandelbots_api_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
8
|
wandelbots_api_client/rest.py,sha256=_X74qGWiiOLYoP_QpNiI9u8kABK4reJYHznf01UKER4,7208
|
|
@@ -325,10 +326,10 @@ wandelbots_api_client/models/virtual_controller.py,sha256=yHZFsjkHsXhMvTyGP3wjQV
|
|
|
325
326
|
wandelbots_api_client/models/virtual_controller_types.py,sha256=4rWSHD3DZj-UypBooESzcB83YmGyQx4fLu8Y5SRgSUQ,5944
|
|
326
327
|
wandelbots_api_client/models/virtual_robot_configuration.py,sha256=KcgjymCdWxcHEXkPpLMkg_Mq7TSiLTM6r2VdPmeW5nc,2952
|
|
327
328
|
wandelbots_api_client/models/yaskawa_controller.py,sha256=_DkR9wvTKrGqMZGIcg0d3cu0G45YSf9iekykTyBKrbE,3203
|
|
328
|
-
wandelbots_api_client/v2/__init__.py,sha256=
|
|
329
|
-
wandelbots_api_client/v2/api_client.py,sha256=
|
|
329
|
+
wandelbots_api_client/v2/__init__.py,sha256=51ZzI0yjjJO5dN4UMwm1YdX7DvenPcpL5GDzOtXbr1w,1111
|
|
330
|
+
wandelbots_api_client/v2/api_client.py,sha256=gm-R8kbnKsnG2ayyKN88qoOcnTANsgqVcRctHJUTxHg,27702
|
|
330
331
|
wandelbots_api_client/v2/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
|
|
331
|
-
wandelbots_api_client/v2/configuration.py,sha256=
|
|
332
|
+
wandelbots_api_client/v2/configuration.py,sha256=aDI_VZYybRzzndGGFRDtylCbKK2_p1odPj8VtAFiX-8,18412
|
|
332
333
|
wandelbots_api_client/v2/exceptions.py,sha256=dAl-6ZWR0W3REJYdNiRpXHF9yH_6QNlr6dWAaUUMM3E,6516
|
|
333
334
|
wandelbots_api_client/v2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
334
335
|
wandelbots_api_client/v2/rest.py,sha256=6a7yb-r1BWnJgC8_9PgI_k_RRX29eJQvltiKCUU5XW0,7299
|
|
@@ -589,8 +590,8 @@ wandelbots_api_client/v2/models/virtual_controller_types.py,sha256=iQ869lF-WLChF
|
|
|
589
590
|
wandelbots_api_client/v2/models/virtual_robot_configuration.py,sha256=VXH1GwZWpysReIBDBFr-ObsEBU8WTl_7r6-POl42opE,3040
|
|
590
591
|
wandelbots_api_client/v2/models/wait_for_io_event_request.py,sha256=fN26w26nC0u7ubmt-TPs2QC0UhA9859P-yheeNvFwuc,3293
|
|
591
592
|
wandelbots_api_client/v2/models/yaskawa_controller.py,sha256=25SUJjdLyy3ZGHsGLhPYQpyGO9Bp2nGT9luk2B7_dA0,3257
|
|
592
|
-
wandelbots_api_client-25.8.0.
|
|
593
|
-
wandelbots_api_client-25.8.0.
|
|
594
|
-
wandelbots_api_client-25.8.0.
|
|
595
|
-
wandelbots_api_client-25.8.0.
|
|
596
|
-
wandelbots_api_client-25.8.0.
|
|
593
|
+
wandelbots_api_client-25.8.0.dev56.dist-info/licenses/LICENSE,sha256=PJyUaZgTVC0_nJmhRpcbnOcXvmTqJGE6axeSifngIGY,11344
|
|
594
|
+
wandelbots_api_client-25.8.0.dev56.dist-info/METADATA,sha256=H4xaS23X_Kh_rKAH6SnqhMJq6KCEcvg0ASeWXlNnA2o,1818
|
|
595
|
+
wandelbots_api_client-25.8.0.dev56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
596
|
+
wandelbots_api_client-25.8.0.dev56.dist-info/top_level.txt,sha256=eCDsV95XWqVBSMDe4MRzyOj53-TK-o5WU1Qv_yv493M,22
|
|
597
|
+
wandelbots_api_client-25.8.0.dev56.dist-info/RECORD,,
|
{wandelbots_api_client-25.8.0.dev55.dist-info → wandelbots_api_client-25.8.0.dev56.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|