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.
@@ -14,7 +14,7 @@
14
14
  """ # noqa: E501
15
15
 
16
16
 
17
- __version__ = "25.8.0.dev55"
17
+ __version__ = "25.8.0.dev56"
18
18
 
19
19
  from . import models
20
20
  from . import api
@@ -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.dev55'
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.dev55".\
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]:
@@ -14,7 +14,7 @@
14
14
  """ # noqa: E501
15
15
 
16
16
 
17
- __version__ = "25.8.0.dev55"
17
+ __version__ = "25.8.0.dev56"
18
18
 
19
19
  from . import models
20
20
  from . import api
@@ -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.dev55'
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.dev55".\
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.dev55
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.dev55
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
 
@@ -1,7 +1,8 @@
1
- wandelbots_api_client/__init__.py,sha256=jDP-Ahgj1sy-yMa2IXSEqfr3A5s6YmXFckGtQtDghVM,1023
2
- wandelbots_api_client/api_client.py,sha256=Ql23JZ5Fnw5J5HEc8QbZaIJ4cl6BiTKRWTcrTCmg8vs,27596
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/configuration.py,sha256=5MGgTKpCl29gUKj19-E_2aRor3s03HMFTUrzKm3I30A,18313
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=k0gZtBiMT44R42zdRI0Y7xIVytFrNUkCHaB_t7141O0,1111
329
- wandelbots_api_client/v2/api_client.py,sha256=EypoRpdWa0VLMBpr5_aUQABBItBHXIDxlhE8HKp1SHo,27702
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=3j2NMKtU5cIsYn7QZ5uzRULhrGgCj5iSE8SROqDupVY,18412
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.dev55.dist-info/licenses/LICENSE,sha256=PJyUaZgTVC0_nJmhRpcbnOcXvmTqJGE6axeSifngIGY,11344
593
- wandelbots_api_client-25.8.0.dev55.dist-info/METADATA,sha256=69NeL0Ac1mXrZuEOO8xsmPzoGsG9JqHHncZljDAHIpA,1818
594
- wandelbots_api_client-25.8.0.dev55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
595
- wandelbots_api_client-25.8.0.dev55.dist-info/top_level.txt,sha256=eCDsV95XWqVBSMDe4MRzyOj53-TK-o5WU1Qv_yv493M,22
596
- wandelbots_api_client-25.8.0.dev55.dist-info/RECORD,,
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,,