stepxmlclient 1.0.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.
Files changed (70) hide show
  1. stepxmlclient/__init__.py +22 -0
  2. stepxmlclient/client.py +373 -0
  3. stepxmlclient/creator.py +317 -0
  4. stepxmlclient/model/__init__.py +418 -0
  5. stepxmlclient/model/asset.py +250 -0
  6. stepxmlclient/model/attribute.py +288 -0
  7. stepxmlclient/model/business.py +233 -0
  8. stepxmlclient/model/classification.py +409 -0
  9. stepxmlclient/model/complex_elements.py +260 -0
  10. stepxmlclient/model/deletion.py +87 -0
  11. stepxmlclient/model/entity.py +368 -0
  12. stepxmlclient/model/metadata.py +199 -0
  13. stepxmlclient/model/organization.py +167 -0
  14. stepxmlclient/model/product.py +519 -0
  15. stepxmlclient/model/relations.py +105 -0
  16. stepxmlclient/model/replacement.py +417 -0
  17. stepxmlclient/parser.py +2805 -0
  18. stepxmlclient/rest_client/__init__.py +16 -0
  19. stepxmlclient/rest_client/base.py +388 -0
  20. stepxmlclient/rest_client/client.py +127 -0
  21. stepxmlclient/rest_client/exceptions.py +97 -0
  22. stepxmlclient/rest_client/models/__init__.py +175 -0
  23. stepxmlclient/rest_client/models/asset.py +253 -0
  24. stepxmlclient/rest_client/models/attribute.py +112 -0
  25. stepxmlclient/rest_client/models/background_process.py +23 -0
  26. stepxmlclient/rest_client/models/batch.py +41 -0
  27. stepxmlclient/rest_client/models/classification.py +57 -0
  28. stepxmlclient/rest_client/models/collection.py +26 -0
  29. stepxmlclient/rest_client/models/common.py +192 -0
  30. stepxmlclient/rest_client/models/context.py +16 -0
  31. stepxmlclient/rest_client/models/data_type_group.py +15 -0
  32. stepxmlclient/rest_client/models/entity.py +63 -0
  33. stepxmlclient/rest_client/models/event_processor.py +15 -0
  34. stepxmlclient/rest_client/models/import_export.py +43 -0
  35. stepxmlclient/rest_client/models/integration_endpoint.py +16 -0
  36. stepxmlclient/rest_client/models/product.py +200 -0
  37. stepxmlclient/rest_client/models/relations.py +280 -0
  38. stepxmlclient/rest_client/models/system_information.py +11 -0
  39. stepxmlclient/rest_client/models/user.py +28 -0
  40. stepxmlclient/rest_client/models/workflow.py +30 -0
  41. stepxmlclient/rest_client/operations/__init__.py +54 -0
  42. stepxmlclient/rest_client/operations/assets.py +652 -0
  43. stepxmlclient/rest_client/operations/attributes.py +252 -0
  44. stepxmlclient/rest_client/operations/background_processes.py +84 -0
  45. stepxmlclient/rest_client/operations/batch.py +28 -0
  46. stepxmlclient/rest_client/operations/classifications.py +248 -0
  47. stepxmlclient/rest_client/operations/collection_groups.py +46 -0
  48. stepxmlclient/rest_client/operations/collections.py +67 -0
  49. stepxmlclient/rest_client/operations/contexts.py +88 -0
  50. stepxmlclient/rest_client/operations/data_type_groups.py +68 -0
  51. stepxmlclient/rest_client/operations/entities.py +295 -0
  52. stepxmlclient/rest_client/operations/event_processors.py +109 -0
  53. stepxmlclient/rest_client/operations/exports.py +153 -0
  54. stepxmlclient/rest_client/operations/gateway.py +53 -0
  55. stepxmlclient/rest_client/operations/inbound.py +472 -0
  56. stepxmlclient/rest_client/operations/outbound.py +64 -0
  57. stepxmlclient/rest_client/operations/products.py +489 -0
  58. stepxmlclient/rest_client/operations/relations.py +349 -0
  59. stepxmlclient/rest_client/operations/reports.py +27 -0
  60. stepxmlclient/rest_client/operations/system_information.py +28 -0
  61. stepxmlclient/rest_client/operations/user_groups.py +79 -0
  62. stepxmlclient/rest_client/operations/users.py +119 -0
  63. stepxmlclient/rest_client/operations/workflow_tasks.py +61 -0
  64. stepxmlclient/rest_client/operations/workflows.py +50 -0
  65. stepxmlclient/upload.py +293 -0
  66. stepxmlclient-1.0.0.dist-info/METADATA +666 -0
  67. stepxmlclient-1.0.0.dist-info/RECORD +70 -0
  68. stepxmlclient-1.0.0.dist-info/WHEEL +5 -0
  69. stepxmlclient-1.0.0.dist-info/licenses/LICENSE +21 -0
  70. stepxmlclient-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,349 @@
1
+ """Reference (relation) operations for STEP REST API v2.
2
+
3
+ In the STEP REST API, references are modeled as sub-resources on entity types:
4
+ GET/PUT/DELETE /products/{id}/references/{referenceTypeId}/{targetId}
5
+ GET/PUT/DELETE /assets/{id}/references/{referenceTypeId}/{targetId}
6
+ GET/PUT/DELETE /classifications/{id}/references/{referenceTypeId}/{targetId}
7
+ GET/PUT/DELETE /entities/{id}/references/{referenceTypeId}/{targetId}
8
+ GET /reference-types/{id}
9
+
10
+ There is no standalone "relationship" or "cross-reference" resource in the spec.
11
+ """
12
+
13
+ import logging
14
+ from typing import Any, Literal, Optional
15
+
16
+ from ..base import BaseOperation, handle_rest_errors
17
+ from ..exceptions import NotFoundError, ValidationError
18
+ from ..models.relations import (
19
+ RelationshipListResponse,
20
+ RelationshipResponse,
21
+ RelationshipTypeResponse,
22
+ )
23
+
24
+ # Supported entity types for reference operations
25
+ EntityType = Literal["products", "assets", "classifications", "entities"]
26
+
27
+ # Map entity type to operation-ID prefix (v1.4.0 operationIds)
28
+ _OP_PREFIX = {
29
+ "products": "Product",
30
+ "assets": "Asset",
31
+ "classifications": "Classification",
32
+ "entities": "Entity",
33
+ }
34
+
35
+ # Map entity type to the noun used in list-reference operation IDs
36
+ _OP_LIST_REF = {
37
+ "products": "ProductReference",
38
+ "assets": "AssetReferences",
39
+ "classifications": "ClassificationReferences",
40
+ "entities": "EntityReferences",
41
+ }
42
+
43
+
44
+ class RelationOperations(BaseOperation):
45
+ """Operations for managing references between STEP entities.
46
+
47
+ The STEP REST API models references as sub-resources attached to a source entity.
48
+ All methods require specifying the ``entity_type`` ('products', 'assets',
49
+ 'classifications', or 'entities') of the *source* object.
50
+ """
51
+
52
+ def __init__(self, client, logger: Optional[logging.Logger] = None,
53
+ context: str | None = None, workspace: str | None = None):
54
+ """Initialize relation operations.
55
+
56
+ Args:
57
+ client: The REST API client
58
+ logger: Optional logger instance
59
+ context: STEP context ID for all API calls
60
+ workspace: STEP workspace ID (defaults to "Main" server-side)
61
+ """
62
+ super().__init__(client, operation_name="RelationOperations", logger=logger,
63
+ context=context, workspace=workspace)
64
+
65
+ # ============================================================================
66
+ # Reference Operations
67
+ # ============================================================================
68
+
69
+ @handle_rest_errors
70
+ def list_references(self, entity_type: EntityType,
71
+ entity_id: str,
72
+ reference_type_id: str,
73
+ key_id: str | None = None) -> RelationshipListResponse:
74
+ """List all outgoing references of a given type for a source entity.
75
+
76
+ Calls: GET /{entity_type}/{id}/references/{referenceTypeId}
77
+
78
+ Args:
79
+ entity_type: Source entity type ('products', 'assets', 'classifications', 'entities')
80
+ entity_id: Source entity ID (or alternate key value if key_id is set)
81
+ reference_type_id: The reference type ID to list
82
+ key_id: Alternate key ID for the source entity (optional)
83
+
84
+ Returns:
85
+ RelationshipListResponse with list of references
86
+ """
87
+ self._validate_required_params(
88
+ {"entity_id": entity_id, "reference_type_id": reference_type_id},
89
+ ["entity_id", "reference_type_id"]
90
+ )
91
+
92
+ list_ref = _OP_LIST_REF.get(entity_type)
93
+ if not list_ref:
94
+ raise ValidationError(f"Invalid entity_type: {entity_type!r}. Must be one of {list(_OP_LIST_REF)}")
95
+ # Spec operationId uses literal braces: e.g. getProductReferenceEntryByReferenceTypeIdAndTargetIdByReferenceTypeId
96
+ op_id = "get" + list_ref + "ByReferenceTypeId"
97
+
98
+ query_params = self._build_context_params()
99
+ if key_id:
100
+ query_params["keyId"] = key_id
101
+ self._log_operation(op_id, {"entity_id": entity_id, "reference_type_id": reference_type_id})
102
+
103
+ try:
104
+ op_func = getattr(self.client, op_id)
105
+ response = op_func(id=entity_id, referenceTypeId=reference_type_id, **query_params)
106
+ except Exception as e:
107
+ if "404" in str(e):
108
+ prefix = _OP_PREFIX.get(entity_type, "Product")
109
+ raise NotFoundError(entity_id, prefix)
110
+ raise
111
+
112
+ if isinstance(response, dict):
113
+ return RelationshipListResponse(**response)
114
+ return response
115
+
116
+ @handle_rest_errors
117
+ def get_reference(self, entity_type: EntityType,
118
+ entity_id: str,
119
+ reference_type_id: str,
120
+ target_id: str,
121
+ key_id: str | None = None,
122
+ target_key_id: str | None = None,
123
+ include_inherited_data: bool = False) -> RelationshipResponse:
124
+ """Get a single outgoing reference.
125
+
126
+ Calls: GET /{entity_type}/{id}/references/{referenceTypeId}/{targetId}
127
+
128
+ Args:
129
+ entity_type: Source entity type
130
+ entity_id: Source entity ID (or alternate key if key_id is set)
131
+ reference_type_id: The reference type ID
132
+ target_id: Target entity ID (or alternate key if target_key_id is set)
133
+ key_id: Alternate key ID for source entity (optional)
134
+ target_key_id: Alternate key ID for target entity (optional)
135
+ include_inherited_data: Whether to include inherited attribute values (default: False)
136
+
137
+ Returns:
138
+ RelationshipResponse with reference details
139
+ """
140
+ self._validate_required_params(
141
+ {"entity_id": entity_id, "reference_type_id": reference_type_id, "target_id": target_id},
142
+ ["entity_id", "reference_type_id", "target_id"]
143
+ )
144
+
145
+ prefix = _OP_PREFIX.get(entity_type, "Product")
146
+ op_id = f"get{prefix}ReferenceEntryByReferenceTypeIdAndTargetId"
147
+
148
+ query_params: dict[str, Any] = self._build_context_params()
149
+ if key_id:
150
+ query_params["keyId"] = key_id
151
+ if target_key_id:
152
+ query_params["targetKeyId"] = target_key_id
153
+ if include_inherited_data:
154
+ query_params["includeInheritedData"] = include_inherited_data
155
+ self._log_operation(op_id, {"entity_id": entity_id, "reference_type_id": reference_type_id, "target_id": target_id})
156
+
157
+ try:
158
+ op_func = getattr(self.client, op_id)
159
+ response = op_func(id=entity_id, referenceTypeId=reference_type_id, targetId=target_id, **query_params)
160
+ except Exception as e:
161
+ if "404" in str(e):
162
+ raise NotFoundError(entity_id, prefix)
163
+ raise
164
+
165
+ if isinstance(response, dict):
166
+ return RelationshipResponse(**response)
167
+ return response
168
+
169
+ @handle_rest_errors
170
+ def put_reference(self, entity_type: EntityType,
171
+ entity_id: str,
172
+ reference_type_id: str,
173
+ target_id: str,
174
+ data: Optional[dict[str, Any]] = None,
175
+ key_id: str | None = None,
176
+ target_key_id: str | None = None,
177
+ allow_overwrite: bool = False) -> RelationshipResponse:
178
+ """Create or replace an outgoing reference (PUT).
179
+
180
+ Calls: PUT /{entity_type}/{id}/references/{referenceTypeId}/{targetId}
181
+
182
+ Args:
183
+ entity_type: Source entity type
184
+ entity_id: Source entity ID (or alternate key if key_id is set)
185
+ reference_type_id: The reference type ID
186
+ target_id: Target entity ID (or alternate key if target_key_id is set)
187
+ data: Optional body with reference attribute values
188
+ key_id: Alternate key ID for source entity (optional)
189
+ target_key_id: Alternate key ID for target entity (optional)
190
+ allow_overwrite: Whether to allow overwriting an existing reference
191
+
192
+ Returns:
193
+ RelationshipResponse with created/replaced reference
194
+ """
195
+ self._validate_required_params(
196
+ {"entity_id": entity_id, "reference_type_id": reference_type_id, "target_id": target_id},
197
+ ["entity_id", "reference_type_id", "target_id"]
198
+ )
199
+
200
+ prefix = _OP_PREFIX.get(entity_type, "Product")
201
+ op_id = f"replace{prefix}ReferenceEntryByReferenceTypeIdAndTargetId"
202
+
203
+ query_params: dict[str, Any] = self._build_context_params()
204
+ if key_id:
205
+ query_params["keyId"] = key_id
206
+ if target_key_id:
207
+ query_params["targetKeyId"] = target_key_id
208
+ if allow_overwrite:
209
+ query_params["allow-overwrite"] = allow_overwrite
210
+ self._log_operation(op_id, {"entity_id": entity_id, "reference_type_id": reference_type_id, "target_id": target_id})
211
+
212
+ try:
213
+ op_func = getattr(self.client, op_id)
214
+ if data:
215
+ response = op_func(id=entity_id, referenceTypeId=reference_type_id, targetId=target_id, json=data, **query_params)
216
+ else:
217
+ response = op_func(id=entity_id, referenceTypeId=reference_type_id, targetId=target_id, **query_params)
218
+ except Exception as e:
219
+ if "404" in str(e):
220
+ raise NotFoundError(entity_id, prefix)
221
+ if "400" in str(e):
222
+ raise ValidationError(str(e))
223
+ raise
224
+
225
+ if isinstance(response, dict):
226
+ return RelationshipResponse(**response)
227
+ return response
228
+
229
+ @handle_rest_errors
230
+ def delete_reference(self, entity_type: EntityType,
231
+ entity_id: str,
232
+ reference_type_id: str,
233
+ target_id: str,
234
+ key_id: str | None = None,
235
+ target_key_id: str | None = None) -> bool:
236
+ """Delete an outgoing reference.
237
+
238
+ Calls: DELETE /{entity_type}/{id}/references/{referenceTypeId}/{targetId}
239
+
240
+ Args:
241
+ entity_type: Source entity type
242
+ entity_id: Source entity ID (or alternate key if key_id is set)
243
+ reference_type_id: The reference type ID
244
+ target_id: Target entity ID (or alternate key if target_key_id is set)
245
+ key_id: Alternate key ID for source entity (optional)
246
+ target_key_id: Alternate key ID for target entity (optional)
247
+
248
+ Returns:
249
+ True if deletion was successful
250
+ """
251
+ self._validate_required_params(
252
+ {"entity_id": entity_id, "reference_type_id": reference_type_id, "target_id": target_id},
253
+ ["entity_id", "reference_type_id", "target_id"]
254
+ )
255
+
256
+ prefix = _OP_PREFIX.get(entity_type, "Product")
257
+ op_id = f"delete{prefix}ReferenceEntryByReferenceTypeIdAndTargetId"
258
+
259
+ query_params = self._build_context_params()
260
+ if key_id:
261
+ query_params["keyId"] = key_id
262
+ if target_key_id:
263
+ query_params["targetKeyId"] = target_key_id
264
+ self._log_operation(op_id, {"entity_id": entity_id, "reference_type_id": reference_type_id, "target_id": target_id})
265
+
266
+ try:
267
+ op_func = getattr(self.client, op_id)
268
+ op_func(id=entity_id, referenceTypeId=reference_type_id, targetId=target_id, **query_params)
269
+ return True
270
+ except Exception as e:
271
+ if "404" in str(e):
272
+ raise NotFoundError(entity_id, prefix)
273
+ raise
274
+
275
+ @handle_rest_errors
276
+ def get_incoming_references(self, entity_type: EntityType,
277
+ entity_id: str,
278
+ reference_type_id: str,
279
+ key_id: str | None = None) -> RelationshipListResponse:
280
+ """List incoming references of a given type for a target entity.
281
+
282
+ Calls: GET /{entity_type}/{id}/incoming-references/{referenceTypeId}
283
+
284
+ Args:
285
+ entity_type: Target entity type
286
+ entity_id: Target entity ID (or alternate key if key_id is set)
287
+ reference_type_id: The reference type ID to list
288
+ key_id: Alternate key ID for the target entity (optional)
289
+
290
+ Returns:
291
+ RelationshipListResponse with list of incoming references
292
+ """
293
+ self._validate_required_params(
294
+ {"entity_id": entity_id, "reference_type_id": reference_type_id},
295
+ ["entity_id", "reference_type_id"]
296
+ )
297
+
298
+ prefix = _OP_PREFIX.get(entity_type, "Product")
299
+ op_id = f"get{prefix}IncomingReferences"
300
+
301
+ query_params = self._build_context_params()
302
+ if key_id:
303
+ query_params["keyId"] = key_id
304
+ self._log_operation(op_id, {"entity_id": entity_id, "reference_type_id": reference_type_id})
305
+
306
+ try:
307
+ op_func = getattr(self.client, op_id)
308
+ response = op_func(id=entity_id, referenceTypeId=reference_type_id, **query_params)
309
+ except Exception as e:
310
+ if "404" in str(e):
311
+ raise NotFoundError(entity_id, prefix)
312
+ raise
313
+
314
+ if isinstance(response, dict):
315
+ return RelationshipListResponse(**response)
316
+ return response
317
+
318
+ # ============================================================================
319
+ # Reference Type Lookup
320
+ # ============================================================================
321
+
322
+ @handle_rest_errors
323
+ def get_reference_type(self, reference_type_id: str) -> RelationshipTypeResponse:
324
+ """Get a reference type definition by ID (GET /reference-types/{id}).
325
+
326
+ Args:
327
+ reference_type_id: The reference type ID
328
+
329
+ Returns:
330
+ RelationshipTypeResponse with reference type details
331
+ """
332
+ self._validate_required_params(
333
+ {"reference_type_id": reference_type_id},
334
+ ["reference_type_id"]
335
+ )
336
+
337
+ query_params = self._build_context_params()
338
+ self._log_operation("getReferenceType", {"reference_type_id": reference_type_id})
339
+
340
+ try:
341
+ response = self.client.getReferenceType(id=reference_type_id, **query_params)
342
+ except Exception as e:
343
+ if "404" in str(e):
344
+ raise NotFoundError(reference_type_id, "ReferenceType")
345
+ raise
346
+
347
+ if isinstance(response, dict):
348
+ return RelationshipTypeResponse(**response)
349
+ return response
@@ -0,0 +1,27 @@
1
+ """Reporting operations for STEP REST API v2."""
2
+ from typing import Any, Optional
3
+
4
+ from ..base import BaseOperation, handle_rest_errors
5
+
6
+
7
+ class ReportOperations(BaseOperation):
8
+ def __init__(self, client, logger=None, context=None, workspace=None):
9
+ super().__init__(client, operation_name="ReportOperations", logger=logger, context=context, workspace=workspace)
10
+
11
+ @handle_rest_errors
12
+ def start_historic_changes_report(self, report_id: str, data: Optional[dict[str, Any]] = None) -> dict[str, Any]:
13
+ self._validate_required_params({"report_id": report_id}, ["report_id"])
14
+ self._log_operation("startHistoricChangesReport", {"reportID": report_id})
15
+ kwargs: dict[str, Any] = {"id": report_id}
16
+ kwargs.update(self._build_context_params())
17
+ if data is not None:
18
+ kwargs["json"] = data
19
+ r = self.client.startHistoricChangesReport(**kwargs)
20
+ return r if isinstance(r, dict) else r
21
+
22
+ @handle_rest_errors
23
+ def clean_up_historic_changes_report(self, report_id: str) -> dict[str, Any]:
24
+ self._validate_required_params({"report_id": report_id}, ["report_id"])
25
+ self._log_operation("cleanUpHistoricChangesReport", {"reportID": report_id})
26
+ r = self.client.cleanUpHistoricChangesReport(id=report_id, **self._build_context_params())
27
+ return r if isinstance(r, dict) else r
@@ -0,0 +1,28 @@
1
+ """System information operations for STEP REST API v2.
2
+
3
+ Covers the /system-information endpoint introduced in v1.4.0.
4
+ """
5
+
6
+ import logging
7
+
8
+ from ..base import BaseOperation, handle_rest_errors
9
+ from ..models.system_information import SystemInformationResponse
10
+
11
+
12
+ class SystemInformationOperations(BaseOperation):
13
+ """Operations for STEP system information."""
14
+
15
+ def __init__(self, client, logger: logging.Logger | None = None,
16
+ context: str | None = None, workspace: str | None = None):
17
+ super().__init__(client, operation_name="SystemInformationOperations", logger=logger,
18
+ context=context, workspace=workspace)
19
+
20
+ @handle_rest_errors
21
+ def get_system_information(self) -> SystemInformationResponse:
22
+ """Get system information and capabilities.
23
+
24
+ Calls: GET /system-information -> getSystemInformation
25
+ """
26
+ self._log_operation("getSystemInformation", {})
27
+ response = self.client.getSystemInformation(**self._build_context_params())
28
+ return SystemInformationResponse(**response) if isinstance(response, dict) else response
@@ -0,0 +1,79 @@
1
+ """User group operations for STEP REST API v2."""
2
+
3
+ from typing import Any
4
+
5
+ from ..base import BaseOperation, handle_rest_errors
6
+ from ..exceptions import NotFoundError, ValidationError
7
+ from ..models.user import UserGroupResponse
8
+
9
+
10
+ class UserGroupOperations(BaseOperation):
11
+ """Operations for STEP User Groups."""
12
+ def __init__(self, client, logger=None, context=None, workspace=None):
13
+ super().__init__(client, operation_name="UserGroupOperations", logger=logger, context=context, workspace=workspace)
14
+
15
+ @handle_rest_errors
16
+ def get_user_group(self, group_id: str) -> UserGroupResponse:
17
+ self._validate_required_params({"group_id": group_id}, ["group_id"])
18
+ self._log_operation("getUserGroup", {"id": group_id})
19
+ try:
20
+ r = self.client.getUserGroup(id=group_id, **self._build_context_params())
21
+ except Exception as e:
22
+ if "404" in str(e): raise NotFoundError(group_id, "UserGroup")
23
+ raise
24
+ return UserGroupResponse(**r) if isinstance(r, dict) else r
25
+
26
+ @handle_rest_errors
27
+ def create_or_replace_user_group(self, group_id: str, data: dict[str, Any]) -> UserGroupResponse:
28
+ self._validate_required_params({"group_id": group_id}, ["group_id"])
29
+ self._log_operation("createOrReplaceUserGroup", {"id": group_id})
30
+ try:
31
+ r = self.client.createOrReplaceUserGroup(id=group_id, json=data, **self._build_context_params())
32
+ except Exception as e:
33
+ if "400" in str(e): raise ValidationError(str(e))
34
+ raise
35
+ return UserGroupResponse(**r) if isinstance(r, dict) else r
36
+
37
+ @handle_rest_errors
38
+ def update_user_group(self, group_id: str, data: dict[str, Any]) -> UserGroupResponse:
39
+ self._validate_required_params({"group_id": group_id}, ["group_id"])
40
+ self._log_operation("updateUserGroup", {"id": group_id})
41
+ try:
42
+ r = self.client.updateUserGroup(id=group_id, json=data, **self._build_context_params())
43
+ except Exception as e:
44
+ if "404" in str(e): raise NotFoundError(group_id, "UserGroup")
45
+ raise
46
+ return UserGroupResponse(**r) if isinstance(r, dict) else r
47
+
48
+ @handle_rest_errors
49
+ def delete_user_group(self, group_id: str) -> bool:
50
+ self._validate_required_params({"group_id": group_id}, ["group_id"])
51
+ self._log_operation("deleteUserGroup", {"id": group_id})
52
+ try:
53
+ self.client.deleteUserGroup(id=group_id, **self._build_context_params())
54
+ return True
55
+ except Exception as e:
56
+ if "404" in str(e): raise NotFoundError(group_id, "UserGroup")
57
+ raise
58
+
59
+ @handle_rest_errors
60
+ def get_user_group_users(self, group_id: str) -> list[dict[str, Any]]:
61
+ self._validate_required_params({"group_id": group_id}, ["group_id"])
62
+ self._log_operation("getUserGroupUsers", {"id": group_id})
63
+ try:
64
+ r = self.client.getUserGroupUsers(id=group_id, **self._build_context_params())
65
+ except Exception as e:
66
+ if "404" in str(e): raise NotFoundError(group_id, "UserGroup")
67
+ raise
68
+ return r if isinstance(r, list) else []
69
+
70
+ @handle_rest_errors
71
+ def get_user_group_children(self, group_id: str) -> list[str]:
72
+ self._validate_required_params({"group_id": group_id}, ["group_id"])
73
+ self._log_operation("getUserGroupChildren", {"id": group_id})
74
+ try:
75
+ r = self.client.getUserGroupChildren(id=group_id, **self._build_context_params())
76
+ except Exception as e:
77
+ if "404" in str(e): raise NotFoundError(group_id, "UserGroup")
78
+ raise
79
+ return r if isinstance(r, list) else []
@@ -0,0 +1,119 @@
1
+ """User operations for STEP REST API v2.
2
+
3
+ Covers the /users endpoints introduced in v1.4.0.
4
+ """
5
+
6
+ import logging
7
+ from typing import Any
8
+
9
+ from ..base import BaseOperation, handle_rest_errors
10
+ from ..exceptions import NotFoundError, ValidationError
11
+ from ..models.user import UserResponse
12
+
13
+
14
+ class UserOperations(BaseOperation):
15
+ """Operations for STEP Users."""
16
+
17
+ def __init__(self, client, logger: logging.Logger | None = None,
18
+ context: str | None = None, workspace: str | None = None):
19
+ super().__init__(client, operation_name="UserOperations", logger=logger,
20
+ context=context, workspace=workspace)
21
+
22
+ @handle_rest_errors
23
+ def get_user(self, user_id: str) -> UserResponse:
24
+ """Get a user by ID.
25
+ Calls: GET /users/{id} -> getUser
26
+ """
27
+ self._validate_required_params({"user_id": user_id}, ["user_id"])
28
+ self._log_operation("getUser", {"id": user_id})
29
+ try:
30
+ r = self.client.getUser(id=user_id, **self._build_context_params())
31
+ except Exception as e:
32
+ if "404" in str(e): raise NotFoundError(user_id, "User")
33
+ raise
34
+ return UserResponse(**r) if isinstance(r, dict) else r
35
+
36
+ @handle_rest_errors
37
+ def create_or_replace_user(self, user_id: str, data: dict[str, Any]) -> UserResponse:
38
+ """Create or fully replace a user.
39
+ Calls: PUT /users/{id} -> createOrReplaceUser
40
+ """
41
+ self._validate_required_params({"user_id": user_id}, ["user_id"])
42
+ self._log_operation("createOrReplaceUser", {"id": user_id})
43
+ try:
44
+ r = self.client.createOrReplaceUser(id=user_id, json=data, **self._build_context_params())
45
+ except Exception as e:
46
+ if "400" in str(e): raise ValidationError(str(e))
47
+ raise
48
+ return UserResponse(**r) if isinstance(r, dict) else r
49
+
50
+ @handle_rest_errors
51
+ def update_user(self, user_id: str, data: dict[str, Any]) -> UserResponse:
52
+ """Partially update a user.
53
+ Calls: PATCH /users/{id} -> updateUser
54
+ """
55
+ self._validate_required_params({"user_id": user_id}, ["user_id"])
56
+ self._log_operation("updateUser", {"id": user_id})
57
+ try:
58
+ r = self.client.updateUser(id=user_id, json=data, **self._build_context_params())
59
+ except Exception as e:
60
+ if "404" in str(e): raise NotFoundError(user_id, "User")
61
+ if "400" in str(e): raise ValidationError(str(e))
62
+ raise
63
+ return UserResponse(**r) if isinstance(r, dict) else r
64
+
65
+ @handle_rest_errors
66
+ def delete_user(self, user_id: str) -> bool:
67
+ """Delete a user.
68
+ Calls: DELETE /users/{id} -> deleteUser
69
+ """
70
+ self._validate_required_params({"user_id": user_id}, ["user_id"])
71
+ self._log_operation("deleteUser", {"id": user_id})
72
+ try:
73
+ self.client.deleteUser(id=user_id, **self._build_context_params())
74
+ return True
75
+ except Exception as e:
76
+ if "404" in str(e): raise NotFoundError(user_id, "User")
77
+ raise
78
+
79
+ @handle_rest_errors
80
+ def randomize_password(self, user_id: str) -> UserResponse:
81
+ """Randomize a user's password.
82
+ Calls: POST /users/{id}/randomize-password -> randomizeStepPassword
83
+ """
84
+ self._validate_required_params({"user_id": user_id}, ["user_id"])
85
+ self._log_operation("randomizeStepPassword", {"id": user_id})
86
+ try:
87
+ r = self.client.randomizeStepPassword(id=user_id, **self._build_context_params())
88
+ except Exception as e:
89
+ if "404" in str(e): raise NotFoundError(user_id, "User")
90
+ raise
91
+ return UserResponse(**r) if isinstance(r, dict) else r
92
+
93
+ @handle_rest_errors
94
+ def add_user_to_group(self, user_id: str, group_data: dict[str, Any]) -> UserResponse:
95
+ """Add a user to a group.
96
+ Calls: POST /users/{id}/add-to-group -> addUserToGroup
97
+ """
98
+ self._validate_required_params({"user_id": user_id}, ["user_id"])
99
+ self._log_operation("addUserToGroup", {"id": user_id})
100
+ try:
101
+ r = self.client.addUserToGroup(id=user_id, json=group_data, **self._build_context_params())
102
+ except Exception as e:
103
+ if "404" in str(e): raise NotFoundError(user_id, "User")
104
+ raise
105
+ return UserResponse(**r) if isinstance(r, dict) else r
106
+
107
+ @handle_rest_errors
108
+ def remove_user_from_group(self, user_id: str, group_data: dict[str, Any]) -> UserResponse:
109
+ """Remove a user from a group.
110
+ Calls: POST /users/{id}/remove-from-group -> removeUserFromGroup
111
+ """
112
+ self._validate_required_params({"user_id": user_id}, ["user_id"])
113
+ self._log_operation("removeUserFromGroup", {"id": user_id})
114
+ try:
115
+ r = self.client.removeUserFromGroup(id=user_id, json=group_data, **self._build_context_params())
116
+ except Exception as e:
117
+ if "404" in str(e): raise NotFoundError(user_id, "User")
118
+ raise
119
+ return UserResponse(**r) if isinstance(r, dict) else r
@@ -0,0 +1,61 @@
1
+ """Workflow task operations for STEP REST API v2."""
2
+ from typing import Any
3
+
4
+ from ..base import BaseOperation, handle_rest_errors
5
+ from ..exceptions import NotFoundError
6
+ from ..models.workflow import WorkflowTaskResponse
7
+
8
+
9
+ class WorkflowTaskOperations(BaseOperation):
10
+ def __init__(self, client, logger=None, context=None, workspace=None):
11
+ super().__init__(client, operation_name="WorkflowTaskOperations", logger=logger, context=context, workspace=workspace)
12
+
13
+ @handle_rest_errors
14
+ def search_workflow_tasks(self, condition: dict[str, Any]) -> WorkflowTaskResponse:
15
+ self._validate_required_params({"condition": condition}, ["condition"])
16
+ self._log_operation("searchWorkflowTasks", {})
17
+ r = self.client.searchWorkflowTasks(json={"condition": condition}, **self._build_context_params())
18
+ return WorkflowTaskResponse(**r) if isinstance(r, dict) else r
19
+
20
+ @handle_rest_errors
21
+ def get_workflow_task(self, task_id: str) -> WorkflowTaskResponse:
22
+ self._validate_required_params({"task_id": task_id}, ["task_id"])
23
+ self._log_operation("getWorkflowTask", {"id": task_id})
24
+ try:
25
+ r = self.client.getWorkflowTask(id=task_id, **self._build_context_params())
26
+ except Exception as e:
27
+ if "404" in str(e): raise NotFoundError(task_id, "WorkflowTask")
28
+ raise
29
+ return WorkflowTaskResponse(**r) if isinstance(r, dict) else r
30
+
31
+ @handle_rest_errors
32
+ def claim_workflow_task(self, task_id: str) -> WorkflowTaskResponse:
33
+ self._validate_required_params({"task_id": task_id}, ["task_id"])
34
+ self._log_operation("claimWorkflowTask", {"id": task_id})
35
+ r = self.client.claimWorkflowTask(id=task_id, **self._build_context_params())
36
+ return WorkflowTaskResponse(**r) if isinstance(r, dict) else r
37
+
38
+ @handle_rest_errors
39
+ def get_workflow_task_events(self, task_id: str) -> list[dict[str, Any]]:
40
+ self._validate_required_params({"task_id": task_id}, ["task_id"])
41
+ self._log_operation("getWorkflowTaskEvents", {"id": task_id})
42
+ try:
43
+ r = self.client.getWorkflowTaskEvents(id=task_id, **self._build_context_params())
44
+ except Exception as e:
45
+ if "404" in str(e): raise NotFoundError(task_id, "WorkflowTask")
46
+ raise
47
+ return r if isinstance(r, list) else []
48
+
49
+ @handle_rest_errors
50
+ def trigger_workflow_task_event(self, task_id: str, data: dict[str, Any]) -> WorkflowTaskResponse:
51
+ self._validate_required_params({"task_id": task_id}, ["task_id"])
52
+ self._log_operation("triggerWorkflowTaskEvent", {"id": task_id})
53
+ r = self.client.triggerWorkflowTaskEvent(id=task_id, json=data, **self._build_context_params())
54
+ return WorkflowTaskResponse(**r) if isinstance(r, dict) else r
55
+
56
+ @handle_rest_errors
57
+ def release_workflow_task(self, task_id: str) -> WorkflowTaskResponse:
58
+ self._validate_required_params({"task_id": task_id}, ["task_id"])
59
+ self._log_operation("releaseWorkflowTask", {"id": task_id})
60
+ r = self.client.releaseWorkflowTask(id=task_id, **self._build_context_params())
61
+ return WorkflowTaskResponse(**r) if isinstance(r, dict) else r