workato-platform-cli 1.0.0rc5.dev5__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 (306) hide show
  1. workato_platform_cli/__init__.py +135 -0
  2. workato_platform_cli/_version.py +34 -0
  3. workato_platform_cli/cli/__init__.py +126 -0
  4. workato_platform_cli/cli/commands/__init__.py +0 -0
  5. workato_platform_cli/cli/commands/api_clients.py +627 -0
  6. workato_platform_cli/cli/commands/api_collections.py +497 -0
  7. workato_platform_cli/cli/commands/assets.py +82 -0
  8. workato_platform_cli/cli/commands/connections.py +1205 -0
  9. workato_platform_cli/cli/commands/connectors/__init__.py +0 -0
  10. workato_platform_cli/cli/commands/connectors/command.py +178 -0
  11. workato_platform_cli/cli/commands/connectors/connector_manager.py +351 -0
  12. workato_platform_cli/cli/commands/data_tables.py +345 -0
  13. workato_platform_cli/cli/commands/guide.py +315 -0
  14. workato_platform_cli/cli/commands/init.py +229 -0
  15. workato_platform_cli/cli/commands/profiles.py +364 -0
  16. workato_platform_cli/cli/commands/projects/__init__.py +0 -0
  17. workato_platform_cli/cli/commands/projects/command.py +513 -0
  18. workato_platform_cli/cli/commands/projects/project_manager.py +338 -0
  19. workato_platform_cli/cli/commands/properties.py +174 -0
  20. workato_platform_cli/cli/commands/pull.py +327 -0
  21. workato_platform_cli/cli/commands/push/__init__.py +0 -0
  22. workato_platform_cli/cli/commands/push/command.py +320 -0
  23. workato_platform_cli/cli/commands/recipes/__init__.py +0 -0
  24. workato_platform_cli/cli/commands/recipes/command.py +847 -0
  25. workato_platform_cli/cli/commands/recipes/validator.py +1740 -0
  26. workato_platform_cli/cli/commands/workspace.py +73 -0
  27. workato_platform_cli/cli/containers.py +80 -0
  28. workato_platform_cli/cli/resources/data/connection-data.json +7364 -0
  29. workato_platform_cli/cli/resources/data/picklist-data.json +3706 -0
  30. workato_platform_cli/cli/resources/docs/README.md +178 -0
  31. workato_platform_cli/cli/resources/docs/actions.md +452 -0
  32. workato_platform_cli/cli/resources/docs/block-structure.md +424 -0
  33. workato_platform_cli/cli/resources/docs/connections-parameters.md +11946 -0
  34. workato_platform_cli/cli/resources/docs/data-mapping.md +779 -0
  35. workato_platform_cli/cli/resources/docs/formulas/array-list-formulas.md +1276 -0
  36. workato_platform_cli/cli/resources/docs/formulas/conditions.md +102 -0
  37. workato_platform_cli/cli/resources/docs/formulas/date-formulas.md +798 -0
  38. workato_platform_cli/cli/resources/docs/formulas/number-formulas.md +507 -0
  39. workato_platform_cli/cli/resources/docs/formulas/other-formulas.md +419 -0
  40. workato_platform_cli/cli/resources/docs/formulas/string-formulas.md +1353 -0
  41. workato_platform_cli/cli/resources/docs/formulas.md +214 -0
  42. workato_platform_cli/cli/resources/docs/naming-conventions.md +163 -0
  43. workato_platform_cli/cli/resources/docs/recipe-deployment-workflow.md +352 -0
  44. workato_platform_cli/cli/resources/docs/recipe-fundamentals.md +179 -0
  45. workato_platform_cli/cli/resources/docs/triggers.md +360 -0
  46. workato_platform_cli/cli/utils/__init__.py +10 -0
  47. workato_platform_cli/cli/utils/config/__init__.py +33 -0
  48. workato_platform_cli/cli/utils/config/manager.py +1001 -0
  49. workato_platform_cli/cli/utils/config/models.py +89 -0
  50. workato_platform_cli/cli/utils/config/profiles.py +491 -0
  51. workato_platform_cli/cli/utils/config/workspace.py +113 -0
  52. workato_platform_cli/cli/utils/exception_handler.py +531 -0
  53. workato_platform_cli/cli/utils/gitignore.py +32 -0
  54. workato_platform_cli/cli/utils/ignore_patterns.py +44 -0
  55. workato_platform_cli/cli/utils/spinner.py +63 -0
  56. workato_platform_cli/cli/utils/version_checker.py +237 -0
  57. workato_platform_cli/client/__init__.py +0 -0
  58. workato_platform_cli/client/workato_api/__init__.py +202 -0
  59. workato_platform_cli/client/workato_api/api/__init__.py +15 -0
  60. workato_platform_cli/client/workato_api/api/api_platform_api.py +2875 -0
  61. workato_platform_cli/client/workato_api/api/connections_api.py +1807 -0
  62. workato_platform_cli/client/workato_api/api/connectors_api.py +840 -0
  63. workato_platform_cli/client/workato_api/api/data_tables_api.py +604 -0
  64. workato_platform_cli/client/workato_api/api/export_api.py +621 -0
  65. workato_platform_cli/client/workato_api/api/folders_api.py +621 -0
  66. workato_platform_cli/client/workato_api/api/packages_api.py +1197 -0
  67. workato_platform_cli/client/workato_api/api/projects_api.py +590 -0
  68. workato_platform_cli/client/workato_api/api/properties_api.py +620 -0
  69. workato_platform_cli/client/workato_api/api/recipes_api.py +1379 -0
  70. workato_platform_cli/client/workato_api/api/users_api.py +285 -0
  71. workato_platform_cli/client/workato_api/api_client.py +807 -0
  72. workato_platform_cli/client/workato_api/api_response.py +21 -0
  73. workato_platform_cli/client/workato_api/configuration.py +601 -0
  74. workato_platform_cli/client/workato_api/docs/APIPlatformApi.md +844 -0
  75. workato_platform_cli/client/workato_api/docs/ApiClient.md +46 -0
  76. workato_platform_cli/client/workato_api/docs/ApiClientApiCollectionsInner.md +30 -0
  77. workato_platform_cli/client/workato_api/docs/ApiClientApiPoliciesInner.md +30 -0
  78. workato_platform_cli/client/workato_api/docs/ApiClientCreateRequest.md +46 -0
  79. workato_platform_cli/client/workato_api/docs/ApiClientListResponse.md +32 -0
  80. workato_platform_cli/client/workato_api/docs/ApiClientResponse.md +29 -0
  81. workato_platform_cli/client/workato_api/docs/ApiCollection.md +38 -0
  82. workato_platform_cli/client/workato_api/docs/ApiCollectionCreateRequest.md +32 -0
  83. workato_platform_cli/client/workato_api/docs/ApiEndpoint.md +41 -0
  84. workato_platform_cli/client/workato_api/docs/ApiKey.md +36 -0
  85. workato_platform_cli/client/workato_api/docs/ApiKeyCreateRequest.md +32 -0
  86. workato_platform_cli/client/workato_api/docs/ApiKeyListResponse.md +32 -0
  87. workato_platform_cli/client/workato_api/docs/ApiKeyResponse.md +29 -0
  88. workato_platform_cli/client/workato_api/docs/Asset.md +39 -0
  89. workato_platform_cli/client/workato_api/docs/AssetReference.md +37 -0
  90. workato_platform_cli/client/workato_api/docs/Connection.md +44 -0
  91. workato_platform_cli/client/workato_api/docs/ConnectionCreateRequest.md +35 -0
  92. workato_platform_cli/client/workato_api/docs/ConnectionUpdateRequest.md +34 -0
  93. workato_platform_cli/client/workato_api/docs/ConnectionsApi.md +526 -0
  94. workato_platform_cli/client/workato_api/docs/ConnectorAction.md +33 -0
  95. workato_platform_cli/client/workato_api/docs/ConnectorVersion.md +32 -0
  96. workato_platform_cli/client/workato_api/docs/ConnectorsApi.md +249 -0
  97. workato_platform_cli/client/workato_api/docs/CreateExportManifestRequest.md +29 -0
  98. workato_platform_cli/client/workato_api/docs/CreateFolderRequest.md +30 -0
  99. workato_platform_cli/client/workato_api/docs/CustomConnector.md +35 -0
  100. workato_platform_cli/client/workato_api/docs/CustomConnectorCodeResponse.md +29 -0
  101. workato_platform_cli/client/workato_api/docs/CustomConnectorCodeResponseData.md +29 -0
  102. workato_platform_cli/client/workato_api/docs/CustomConnectorListResponse.md +29 -0
  103. workato_platform_cli/client/workato_api/docs/DataTable.md +34 -0
  104. workato_platform_cli/client/workato_api/docs/DataTableColumn.md +37 -0
  105. workato_platform_cli/client/workato_api/docs/DataTableColumnRequest.md +37 -0
  106. workato_platform_cli/client/workato_api/docs/DataTableCreateRequest.md +31 -0
  107. workato_platform_cli/client/workato_api/docs/DataTableCreateResponse.md +29 -0
  108. workato_platform_cli/client/workato_api/docs/DataTableListResponse.md +29 -0
  109. workato_platform_cli/client/workato_api/docs/DataTableRelation.md +30 -0
  110. workato_platform_cli/client/workato_api/docs/DataTablesApi.md +172 -0
  111. workato_platform_cli/client/workato_api/docs/DeleteProject403Response.md +29 -0
  112. workato_platform_cli/client/workato_api/docs/Error.md +29 -0
  113. workato_platform_cli/client/workato_api/docs/ExportApi.md +175 -0
  114. workato_platform_cli/client/workato_api/docs/ExportManifestRequest.md +35 -0
  115. workato_platform_cli/client/workato_api/docs/ExportManifestResponse.md +29 -0
  116. workato_platform_cli/client/workato_api/docs/ExportManifestResponseResult.md +36 -0
  117. workato_platform_cli/client/workato_api/docs/Folder.md +35 -0
  118. workato_platform_cli/client/workato_api/docs/FolderAssetsResponse.md +29 -0
  119. workato_platform_cli/client/workato_api/docs/FolderAssetsResponseResult.md +29 -0
  120. workato_platform_cli/client/workato_api/docs/FolderCreationResponse.md +35 -0
  121. workato_platform_cli/client/workato_api/docs/FoldersApi.md +176 -0
  122. workato_platform_cli/client/workato_api/docs/ImportResults.md +32 -0
  123. workato_platform_cli/client/workato_api/docs/OAuthUrlResponse.md +29 -0
  124. workato_platform_cli/client/workato_api/docs/OAuthUrlResponseData.md +29 -0
  125. workato_platform_cli/client/workato_api/docs/OpenApiSpec.md +30 -0
  126. workato_platform_cli/client/workato_api/docs/PackageDetailsResponse.md +35 -0
  127. workato_platform_cli/client/workato_api/docs/PackageDetailsResponseRecipeStatusInner.md +30 -0
  128. workato_platform_cli/client/workato_api/docs/PackageResponse.md +33 -0
  129. workato_platform_cli/client/workato_api/docs/PackagesApi.md +364 -0
  130. workato_platform_cli/client/workato_api/docs/PicklistRequest.md +30 -0
  131. workato_platform_cli/client/workato_api/docs/PicklistResponse.md +29 -0
  132. workato_platform_cli/client/workato_api/docs/PlatformConnector.md +36 -0
  133. workato_platform_cli/client/workato_api/docs/PlatformConnectorListResponse.md +32 -0
  134. workato_platform_cli/client/workato_api/docs/Project.md +32 -0
  135. workato_platform_cli/client/workato_api/docs/ProjectsApi.md +173 -0
  136. workato_platform_cli/client/workato_api/docs/PropertiesApi.md +186 -0
  137. workato_platform_cli/client/workato_api/docs/Recipe.md +58 -0
  138. workato_platform_cli/client/workato_api/docs/RecipeConfigInner.md +33 -0
  139. workato_platform_cli/client/workato_api/docs/RecipeConnectionUpdateRequest.md +30 -0
  140. workato_platform_cli/client/workato_api/docs/RecipeListResponse.md +29 -0
  141. workato_platform_cli/client/workato_api/docs/RecipeStartResponse.md +31 -0
  142. workato_platform_cli/client/workato_api/docs/RecipesApi.md +367 -0
  143. workato_platform_cli/client/workato_api/docs/RuntimeUserConnectionCreateRequest.md +34 -0
  144. workato_platform_cli/client/workato_api/docs/RuntimeUserConnectionResponse.md +29 -0
  145. workato_platform_cli/client/workato_api/docs/RuntimeUserConnectionResponseData.md +30 -0
  146. workato_platform_cli/client/workato_api/docs/SuccessResponse.md +29 -0
  147. workato_platform_cli/client/workato_api/docs/UpsertProjectPropertiesRequest.md +29 -0
  148. workato_platform_cli/client/workato_api/docs/User.md +48 -0
  149. workato_platform_cli/client/workato_api/docs/UsersApi.md +84 -0
  150. workato_platform_cli/client/workato_api/docs/ValidationError.md +30 -0
  151. workato_platform_cli/client/workato_api/docs/ValidationErrorErrorsValue.md +28 -0
  152. workato_platform_cli/client/workato_api/exceptions.py +216 -0
  153. workato_platform_cli/client/workato_api/models/__init__.py +83 -0
  154. workato_platform_cli/client/workato_api/models/api_client.py +185 -0
  155. workato_platform_cli/client/workato_api/models/api_client_api_collections_inner.py +89 -0
  156. workato_platform_cli/client/workato_api/models/api_client_api_policies_inner.py +89 -0
  157. workato_platform_cli/client/workato_api/models/api_client_create_request.py +138 -0
  158. workato_platform_cli/client/workato_api/models/api_client_list_response.py +101 -0
  159. workato_platform_cli/client/workato_api/models/api_client_response.py +91 -0
  160. workato_platform_cli/client/workato_api/models/api_collection.py +110 -0
  161. workato_platform_cli/client/workato_api/models/api_collection_create_request.py +97 -0
  162. workato_platform_cli/client/workato_api/models/api_endpoint.py +117 -0
  163. workato_platform_cli/client/workato_api/models/api_key.py +102 -0
  164. workato_platform_cli/client/workato_api/models/api_key_create_request.py +93 -0
  165. workato_platform_cli/client/workato_api/models/api_key_list_response.py +101 -0
  166. workato_platform_cli/client/workato_api/models/api_key_response.py +91 -0
  167. workato_platform_cli/client/workato_api/models/asset.py +124 -0
  168. workato_platform_cli/client/workato_api/models/asset_reference.py +110 -0
  169. workato_platform_cli/client/workato_api/models/connection.py +173 -0
  170. workato_platform_cli/client/workato_api/models/connection_create_request.py +99 -0
  171. workato_platform_cli/client/workato_api/models/connection_update_request.py +97 -0
  172. workato_platform_cli/client/workato_api/models/connector_action.py +100 -0
  173. workato_platform_cli/client/workato_api/models/connector_version.py +99 -0
  174. workato_platform_cli/client/workato_api/models/create_export_manifest_request.py +91 -0
  175. workato_platform_cli/client/workato_api/models/create_folder_request.py +89 -0
  176. workato_platform_cli/client/workato_api/models/custom_connector.py +117 -0
  177. workato_platform_cli/client/workato_api/models/custom_connector_code_response.py +91 -0
  178. workato_platform_cli/client/workato_api/models/custom_connector_code_response_data.py +87 -0
  179. workato_platform_cli/client/workato_api/models/custom_connector_list_response.py +95 -0
  180. workato_platform_cli/client/workato_api/models/data_table.py +107 -0
  181. workato_platform_cli/client/workato_api/models/data_table_column.py +125 -0
  182. workato_platform_cli/client/workato_api/models/data_table_column_request.py +130 -0
  183. workato_platform_cli/client/workato_api/models/data_table_create_request.py +99 -0
  184. workato_platform_cli/client/workato_api/models/data_table_create_response.py +91 -0
  185. workato_platform_cli/client/workato_api/models/data_table_list_response.py +95 -0
  186. workato_platform_cli/client/workato_api/models/data_table_relation.py +90 -0
  187. workato_platform_cli/client/workato_api/models/delete_project403_response.py +87 -0
  188. workato_platform_cli/client/workato_api/models/error.py +87 -0
  189. workato_platform_cli/client/workato_api/models/export_manifest_request.py +107 -0
  190. workato_platform_cli/client/workato_api/models/export_manifest_response.py +91 -0
  191. workato_platform_cli/client/workato_api/models/export_manifest_response_result.py +112 -0
  192. workato_platform_cli/client/workato_api/models/folder.py +110 -0
  193. workato_platform_cli/client/workato_api/models/folder_assets_response.py +91 -0
  194. workato_platform_cli/client/workato_api/models/folder_assets_response_result.py +95 -0
  195. workato_platform_cli/client/workato_api/models/folder_creation_response.py +110 -0
  196. workato_platform_cli/client/workato_api/models/import_results.py +93 -0
  197. workato_platform_cli/client/workato_api/models/o_auth_url_response.py +91 -0
  198. workato_platform_cli/client/workato_api/models/o_auth_url_response_data.py +87 -0
  199. workato_platform_cli/client/workato_api/models/open_api_spec.py +96 -0
  200. workato_platform_cli/client/workato_api/models/package_details_response.py +126 -0
  201. workato_platform_cli/client/workato_api/models/package_details_response_recipe_status_inner.py +99 -0
  202. workato_platform_cli/client/workato_api/models/package_response.py +109 -0
  203. workato_platform_cli/client/workato_api/models/picklist_request.py +89 -0
  204. workato_platform_cli/client/workato_api/models/picklist_response.py +88 -0
  205. workato_platform_cli/client/workato_api/models/platform_connector.py +116 -0
  206. workato_platform_cli/client/workato_api/models/platform_connector_list_response.py +101 -0
  207. workato_platform_cli/client/workato_api/models/project.py +93 -0
  208. workato_platform_cli/client/workato_api/models/recipe.py +174 -0
  209. workato_platform_cli/client/workato_api/models/recipe_config_inner.py +100 -0
  210. workato_platform_cli/client/workato_api/models/recipe_connection_update_request.py +89 -0
  211. workato_platform_cli/client/workato_api/models/recipe_list_response.py +95 -0
  212. workato_platform_cli/client/workato_api/models/recipe_start_response.py +91 -0
  213. workato_platform_cli/client/workato_api/models/runtime_user_connection_create_request.py +97 -0
  214. workato_platform_cli/client/workato_api/models/runtime_user_connection_response.py +91 -0
  215. workato_platform_cli/client/workato_api/models/runtime_user_connection_response_data.py +89 -0
  216. workato_platform_cli/client/workato_api/models/success_response.py +87 -0
  217. workato_platform_cli/client/workato_api/models/upsert_project_properties_request.py +88 -0
  218. workato_platform_cli/client/workato_api/models/user.py +151 -0
  219. workato_platform_cli/client/workato_api/models/validation_error.py +102 -0
  220. workato_platform_cli/client/workato_api/models/validation_error_errors_value.py +143 -0
  221. workato_platform_cli/client/workato_api/rest.py +213 -0
  222. workato_platform_cli/client/workato_api/test/__init__.py +0 -0
  223. workato_platform_cli/client/workato_api/test/test_api_client.py +94 -0
  224. workato_platform_cli/client/workato_api/test/test_api_client_api_collections_inner.py +52 -0
  225. workato_platform_cli/client/workato_api/test/test_api_client_api_policies_inner.py +52 -0
  226. workato_platform_cli/client/workato_api/test/test_api_client_create_request.py +75 -0
  227. workato_platform_cli/client/workato_api/test/test_api_client_list_response.py +114 -0
  228. workato_platform_cli/client/workato_api/test/test_api_client_response.py +104 -0
  229. workato_platform_cli/client/workato_api/test/test_api_collection.py +72 -0
  230. workato_platform_cli/client/workato_api/test/test_api_collection_create_request.py +57 -0
  231. workato_platform_cli/client/workato_api/test/test_api_endpoint.py +75 -0
  232. workato_platform_cli/client/workato_api/test/test_api_key.py +64 -0
  233. workato_platform_cli/client/workato_api/test/test_api_key_create_request.py +56 -0
  234. workato_platform_cli/client/workato_api/test/test_api_key_list_response.py +78 -0
  235. workato_platform_cli/client/workato_api/test/test_api_key_response.py +68 -0
  236. workato_platform_cli/client/workato_api/test/test_api_platform_api.py +101 -0
  237. workato_platform_cli/client/workato_api/test/test_asset.py +67 -0
  238. workato_platform_cli/client/workato_api/test/test_asset_reference.py +62 -0
  239. workato_platform_cli/client/workato_api/test/test_connection.py +81 -0
  240. workato_platform_cli/client/workato_api/test/test_connection_create_request.py +59 -0
  241. workato_platform_cli/client/workato_api/test/test_connection_update_request.py +56 -0
  242. workato_platform_cli/client/workato_api/test/test_connections_api.py +73 -0
  243. workato_platform_cli/client/workato_api/test/test_connector_action.py +59 -0
  244. workato_platform_cli/client/workato_api/test/test_connector_version.py +58 -0
  245. workato_platform_cli/client/workato_api/test/test_connectors_api.py +52 -0
  246. workato_platform_cli/client/workato_api/test/test_create_export_manifest_request.py +88 -0
  247. workato_platform_cli/client/workato_api/test/test_create_folder_request.py +53 -0
  248. workato_platform_cli/client/workato_api/test/test_custom_connector.py +76 -0
  249. workato_platform_cli/client/workato_api/test/test_custom_connector_code_response.py +54 -0
  250. workato_platform_cli/client/workato_api/test/test_custom_connector_code_response_data.py +52 -0
  251. workato_platform_cli/client/workato_api/test/test_custom_connector_list_response.py +82 -0
  252. workato_platform_cli/client/workato_api/test/test_data_table.py +88 -0
  253. workato_platform_cli/client/workato_api/test/test_data_table_column.py +72 -0
  254. workato_platform_cli/client/workato_api/test/test_data_table_column_request.py +64 -0
  255. workato_platform_cli/client/workato_api/test/test_data_table_create_request.py +82 -0
  256. workato_platform_cli/client/workato_api/test/test_data_table_create_response.py +90 -0
  257. workato_platform_cli/client/workato_api/test/test_data_table_list_response.py +94 -0
  258. workato_platform_cli/client/workato_api/test/test_data_table_relation.py +54 -0
  259. workato_platform_cli/client/workato_api/test/test_data_tables_api.py +45 -0
  260. workato_platform_cli/client/workato_api/test/test_delete_project403_response.py +51 -0
  261. workato_platform_cli/client/workato_api/test/test_error.py +52 -0
  262. workato_platform_cli/client/workato_api/test/test_export_api.py +45 -0
  263. workato_platform_cli/client/workato_api/test/test_export_manifest_request.py +69 -0
  264. workato_platform_cli/client/workato_api/test/test_export_manifest_response.py +68 -0
  265. workato_platform_cli/client/workato_api/test/test_export_manifest_response_result.py +66 -0
  266. workato_platform_cli/client/workato_api/test/test_folder.py +64 -0
  267. workato_platform_cli/client/workato_api/test/test_folder_assets_response.py +80 -0
  268. workato_platform_cli/client/workato_api/test/test_folder_assets_response_result.py +78 -0
  269. workato_platform_cli/client/workato_api/test/test_folder_creation_response.py +64 -0
  270. workato_platform_cli/client/workato_api/test/test_folders_api.py +45 -0
  271. workato_platform_cli/client/workato_api/test/test_import_results.py +58 -0
  272. workato_platform_cli/client/workato_api/test/test_o_auth_url_response.py +54 -0
  273. workato_platform_cli/client/workato_api/test/test_o_auth_url_response_data.py +52 -0
  274. workato_platform_cli/client/workato_api/test/test_open_api_spec.py +54 -0
  275. workato_platform_cli/client/workato_api/test/test_package_details_response.py +64 -0
  276. workato_platform_cli/client/workato_api/test/test_package_details_response_recipe_status_inner.py +52 -0
  277. workato_platform_cli/client/workato_api/test/test_package_response.py +58 -0
  278. workato_platform_cli/client/workato_api/test/test_packages_api.py +59 -0
  279. workato_platform_cli/client/workato_api/test/test_picklist_request.py +53 -0
  280. workato_platform_cli/client/workato_api/test/test_picklist_response.py +52 -0
  281. workato_platform_cli/client/workato_api/test/test_platform_connector.py +94 -0
  282. workato_platform_cli/client/workato_api/test/test_platform_connector_list_response.py +106 -0
  283. workato_platform_cli/client/workato_api/test/test_project.py +57 -0
  284. workato_platform_cli/client/workato_api/test/test_projects_api.py +45 -0
  285. workato_platform_cli/client/workato_api/test/test_properties_api.py +45 -0
  286. workato_platform_cli/client/workato_api/test/test_recipe.py +124 -0
  287. workato_platform_cli/client/workato_api/test/test_recipe_config_inner.py +55 -0
  288. workato_platform_cli/client/workato_api/test/test_recipe_connection_update_request.py +54 -0
  289. workato_platform_cli/client/workato_api/test/test_recipe_list_response.py +134 -0
  290. workato_platform_cli/client/workato_api/test/test_recipe_start_response.py +54 -0
  291. workato_platform_cli/client/workato_api/test/test_recipes_api.py +59 -0
  292. workato_platform_cli/client/workato_api/test/test_runtime_user_connection_create_request.py +59 -0
  293. workato_platform_cli/client/workato_api/test/test_runtime_user_connection_response.py +56 -0
  294. workato_platform_cli/client/workato_api/test/test_runtime_user_connection_response_data.py +54 -0
  295. workato_platform_cli/client/workato_api/test/test_success_response.py +52 -0
  296. workato_platform_cli/client/workato_api/test/test_upsert_project_properties_request.py +52 -0
  297. workato_platform_cli/client/workato_api/test/test_user.py +85 -0
  298. workato_platform_cli/client/workato_api/test/test_users_api.py +38 -0
  299. workato_platform_cli/client/workato_api/test/test_validation_error.py +52 -0
  300. workato_platform_cli/client/workato_api/test/test_validation_error_errors_value.py +50 -0
  301. workato_platform_cli/client/workato_api_README.md +205 -0
  302. workato_platform_cli-1.0.0rc5.dev5.dist-info/METADATA +185 -0
  303. workato_platform_cli-1.0.0rc5.dev5.dist-info/RECORD +306 -0
  304. workato_platform_cli-1.0.0rc5.dev5.dist-info/WHEEL +4 -0
  305. workato_platform_cli-1.0.0rc5.dev5.dist-info/entry_points.txt +2 -0
  306. workato_platform_cli-1.0.0rc5.dev5.dist-info/licenses/LICENSE +7 -0
@@ -0,0 +1,531 @@
1
+ """Exception handler decorator for user-friendly error messages."""
2
+
3
+ import asyncio
4
+ import functools
5
+ import json
6
+ import ssl
7
+
8
+ from collections.abc import Callable
9
+ from json import JSONDecodeError
10
+ from typing import Any, TypeVar, cast
11
+
12
+ import aiohttp
13
+ import asyncclick as click
14
+
15
+ from workato_platform_cli.client.workato_api.exceptions import (
16
+ ApiException,
17
+ BadRequestException,
18
+ ConflictException,
19
+ ForbiddenException,
20
+ NotFoundException,
21
+ ServiceException,
22
+ UnauthorizedException,
23
+ UnprocessableEntityException,
24
+ )
25
+
26
+
27
+ F = TypeVar("F", bound=Callable[..., Any])
28
+
29
+
30
+ def handle_api_exceptions(func: F) -> F:
31
+ """Decorator to handle workato_api exceptions with user-friendly messages.
32
+
33
+ This decorator catches HTTP exceptions from the Workato API client and
34
+ displays user-friendly error messages instead of raw stack traces.
35
+
36
+ Supports both sync and async functions.
37
+
38
+ Usage:
39
+ @handle_api_exceptions
40
+ async def my_command():
41
+ # Your command logic here
42
+ pass
43
+ """
44
+
45
+ if asyncio.iscoroutinefunction(func):
46
+
47
+ @functools.wraps(func)
48
+ async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
49
+ try:
50
+ return await func(*args, **kwargs)
51
+ except (BadRequestException, UnprocessableEntityException) as e:
52
+ _handle_client_error(e)
53
+ raise SystemExit(1) from None
54
+ except UnauthorizedException as e:
55
+ _handle_auth_error(e)
56
+ raise SystemExit(1) from None
57
+ except ForbiddenException as e:
58
+ _handle_forbidden_error(e)
59
+ raise SystemExit(1) from None
60
+ except NotFoundException as e:
61
+ _handle_not_found_error(e)
62
+ raise SystemExit(1) from None
63
+ except ConflictException as e:
64
+ _handle_conflict_error(e)
65
+ raise SystemExit(1) from None
66
+ except ServiceException as e:
67
+ _handle_server_error(e)
68
+ raise SystemExit(1) from None
69
+ except ApiException as e:
70
+ _handle_generic_api_error(e)
71
+ raise SystemExit(1) from None
72
+
73
+ return cast(F, async_wrapper)
74
+ else:
75
+
76
+ @functools.wraps(func)
77
+ def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
78
+ try:
79
+ return func(*args, **kwargs)
80
+ except (BadRequestException, UnprocessableEntityException) as e:
81
+ _handle_client_error(e)
82
+ raise SystemExit(1) from None
83
+ except UnauthorizedException as e:
84
+ _handle_auth_error(e)
85
+ raise SystemExit(1) from None
86
+ except ForbiddenException as e:
87
+ _handle_forbidden_error(e)
88
+ raise SystemExit(1) from None
89
+ except NotFoundException as e:
90
+ _handle_not_found_error(e)
91
+ raise SystemExit(1) from None
92
+ except ConflictException as e:
93
+ _handle_conflict_error(e)
94
+ raise SystemExit(1) from None
95
+ except ServiceException as e:
96
+ _handle_server_error(e)
97
+ raise SystemExit(1) from None
98
+ except ApiException as e:
99
+ _handle_generic_api_error(e)
100
+ raise SystemExit(1) from None
101
+
102
+ return cast(F, sync_wrapper)
103
+
104
+
105
+ def handle_cli_exceptions(func: F) -> F:
106
+ """Handle CLI initialization and network errors with friendly messages.
107
+
108
+ This decorator catches errors that occur during dependency injection and CLI
109
+ initialization, such as missing credentials, network failures, and configuration
110
+ errors. It should be placed above @inject in the decorator stack.
111
+
112
+ Supports both sync and async functions.
113
+
114
+ Usage:
115
+ @click.command()
116
+ @handle_cli_exceptions
117
+ @inject
118
+ @handle_api_exceptions
119
+ async def my_command():
120
+ # Your command logic here
121
+ pass
122
+ """
123
+
124
+ if asyncio.iscoroutinefunction(func):
125
+
126
+ @functools.wraps(func)
127
+ async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
128
+ try:
129
+ return await func(*args, **kwargs)
130
+ except (
131
+ aiohttp.ClientConnectorError,
132
+ aiohttp.ClientConnectionError,
133
+ ) as e:
134
+ _handle_network_error(e)
135
+ raise SystemExit(1) from None
136
+ except TimeoutError as e:
137
+ _handle_timeout_error(e)
138
+ raise SystemExit(1) from None
139
+ except aiohttp.ServerDisconnectedError as e:
140
+ _handle_server_disconnect_error(e)
141
+ raise SystemExit(1) from None
142
+ except (aiohttp.ClientSSLError, ssl.SSLError) as e:
143
+ _handle_ssl_error(e)
144
+ raise SystemExit(1) from None
145
+ except click.Abort:
146
+ # Let Click handle Abort - don't catch it
147
+ raise
148
+ except Exception as e:
149
+ # Catch-all for any exceptions during initialization
150
+ _handle_generic_cli_error(e)
151
+ raise SystemExit(1) from None
152
+
153
+ return cast(F, async_wrapper)
154
+ else:
155
+
156
+ @functools.wraps(func)
157
+ def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
158
+ try:
159
+ return func(*args, **kwargs)
160
+ except (
161
+ aiohttp.ClientConnectorError,
162
+ aiohttp.ClientConnectionError,
163
+ ) as e:
164
+ _handle_network_error(e)
165
+ raise SystemExit(1) from None
166
+ except TimeoutError as e:
167
+ _handle_timeout_error(e)
168
+ raise SystemExit(1) from None
169
+ except aiohttp.ServerDisconnectedError as e:
170
+ _handle_server_disconnect_error(e)
171
+ raise SystemExit(1) from None
172
+ except (aiohttp.ClientSSLError, ssl.SSLError) as e:
173
+ _handle_ssl_error(e)
174
+ raise SystemExit(1) from None
175
+ except click.Abort:
176
+ # Let Click handle Abort - don't catch it
177
+ raise
178
+ except Exception as e:
179
+ # Catch-all for any exceptions during initialization
180
+ _handle_generic_cli_error(e)
181
+ raise SystemExit(1) from None
182
+
183
+ return cast(F, sync_wrapper)
184
+
185
+
186
+ def _get_output_mode() -> str:
187
+ """Get the output mode from Click context."""
188
+ ctx = click.get_current_context(silent=True)
189
+ if ctx and hasattr(ctx, "params"):
190
+ output_mode: str = ctx.params.get("output_mode", "table")
191
+ return output_mode
192
+ return "table"
193
+
194
+
195
+ def _handle_client_error(
196
+ e: BadRequestException | UnprocessableEntityException,
197
+ ) -> None:
198
+ """Handle 400 Bad Request and 422 Unprocessable Entity errors."""
199
+ output_mode = _get_output_mode()
200
+
201
+ if output_mode == "json":
202
+ error_details = _extract_error_details(e)
203
+ error_data = {
204
+ "status": "error",
205
+ "error": error_details
206
+ or e.reason
207
+ or "Bad request - check your input parameters",
208
+ "error_code": "BAD_REQUEST"
209
+ if isinstance(e, BadRequestException)
210
+ else "UNPROCESSABLE_ENTITY",
211
+ }
212
+ click.echo(json.dumps(error_data))
213
+ return
214
+
215
+ click.echo("❌ Invalid request")
216
+
217
+ # Try to extract error details from response body
218
+ error_details = _extract_error_details(e)
219
+ if error_details:
220
+ click.echo(f" {error_details}")
221
+ else:
222
+ click.echo(f" {e.reason or 'Bad request - check your input parameters'}")
223
+
224
+ click.echo("💡 Please check your input and try again")
225
+
226
+
227
+ def _handle_auth_error(e: UnauthorizedException) -> None:
228
+ """Handle 401 Unauthorized errors."""
229
+ output_mode = _get_output_mode()
230
+
231
+ if output_mode == "json":
232
+ error_data = {
233
+ "status": "error",
234
+ "error": "Authentication failed - invalid or missing API token",
235
+ "error_code": "UNAUTHORIZED",
236
+ }
237
+ click.echo(json.dumps(error_data))
238
+ return
239
+
240
+ click.echo("❌ Authentication failed")
241
+ click.echo(" Your API token may be invalid")
242
+ click.echo("💡 Please check your authentication:")
243
+ click.echo(" • Verify your API token is correct")
244
+ click.echo(" • Run 'workato profiles list' to check your profile")
245
+ click.echo(" • Run 'workato profiles use' to update your credentials")
246
+
247
+
248
+ def _handle_forbidden_error(e: ForbiddenException) -> None:
249
+ """Handle 403 Forbidden errors."""
250
+ output_mode = _get_output_mode()
251
+
252
+ if output_mode == "json":
253
+ error_details = _extract_error_details(e)
254
+ error_data = {
255
+ "status": "error",
256
+ "error": error_details or "Access forbidden - insufficient permissions",
257
+ "error_code": "FORBIDDEN",
258
+ }
259
+ click.echo(json.dumps(error_data))
260
+ return
261
+
262
+ click.echo("❌ Access forbidden")
263
+ click.echo(" You don't have permission to perform this action")
264
+
265
+ error_details = _extract_error_details(e)
266
+ if error_details:
267
+ click.echo(f" {error_details}")
268
+
269
+ click.echo("💡 Please check:")
270
+ click.echo(" • Your account has the required permissions")
271
+ click.echo(" • You're working in the correct workspace/folder")
272
+ click.echo(" • The resource exists and is accessible to you")
273
+
274
+
275
+ def _handle_not_found_error(e: NotFoundException) -> None:
276
+ """Handle 404 Not Found errors."""
277
+ output_mode = _get_output_mode()
278
+
279
+ if output_mode == "json":
280
+ error_details = _extract_error_details(e)
281
+ error_data = {
282
+ "status": "error",
283
+ "error": error_details or "Resource not found",
284
+ "error_code": "NOT_FOUND",
285
+ }
286
+ click.echo(json.dumps(error_data))
287
+ return
288
+
289
+ click.echo("❌ Resource not found")
290
+ click.echo(" The requested resource could not be found")
291
+
292
+ error_details = _extract_error_details(e)
293
+ if error_details:
294
+ click.echo(f" {error_details}")
295
+
296
+ click.echo("💡 Please check:")
297
+ click.echo(" • The ID or name is correct")
298
+ click.echo(" • The resource exists in your workspace")
299
+ click.echo(" • You have permission to access the resource")
300
+
301
+
302
+ def _handle_conflict_error(e: ConflictException) -> None:
303
+ """Handle 409 Conflict errors."""
304
+ output_mode = _get_output_mode()
305
+
306
+ if output_mode == "json":
307
+ error_details = _extract_error_details(e)
308
+ error_data = {
309
+ "status": "error",
310
+ "error": error_details or "Request conflicts with current state",
311
+ "error_code": "CONFLICT",
312
+ }
313
+ click.echo(json.dumps(error_data))
314
+ return
315
+
316
+ click.echo("❌ Conflict detected")
317
+ click.echo(" The request conflicts with the current state")
318
+
319
+ error_details = _extract_error_details(e)
320
+ if error_details:
321
+ click.echo(f" {error_details}")
322
+
323
+ click.echo("💡 This usually means:")
324
+ click.echo(" • A resource with the same name already exists")
325
+ click.echo(" • The resource is being used by another process")
326
+ click.echo(" • There's a version conflict")
327
+
328
+
329
+ def _handle_server_error(e: ServiceException) -> None:
330
+ """Handle 5xx Server errors."""
331
+ output_mode = _get_output_mode()
332
+
333
+ if output_mode == "json":
334
+ error_data = {
335
+ "status": "error",
336
+ "error": "Server error - Workato API is experiencing issues",
337
+ "error_code": "SERVER_ERROR",
338
+ "http_status": e.status,
339
+ }
340
+ click.echo(json.dumps(error_data))
341
+ return
342
+
343
+ click.echo("❌ Server error")
344
+ click.echo(" The Workato API is experiencing issues")
345
+ click.echo(f" Status: {e.status}")
346
+
347
+ click.echo("💡 Please try:")
348
+ click.echo(" • Wait a few moments and retry")
349
+ click.echo(" • Check Workato status page for outages")
350
+ click.echo(" • Contact support if the issue persists")
351
+
352
+
353
+ def _handle_generic_api_error(e: ApiException) -> None:
354
+ """Handle other API errors."""
355
+ output_mode = _get_output_mode()
356
+
357
+ if output_mode == "json":
358
+ error_details = _extract_error_details(e)
359
+ error_data = {
360
+ "status": "error",
361
+ "error": error_details or e.reason or "API error occurred",
362
+ "error_code": "API_ERROR",
363
+ }
364
+ if e.status:
365
+ error_data["http_status"] = e.status
366
+ click.echo(json.dumps(error_data))
367
+ return
368
+
369
+ click.echo("❌ API error occurred")
370
+
371
+ if e.status:
372
+ click.echo(f" Status: {e.status}")
373
+ if e.reason:
374
+ click.echo(f" Reason: {e.reason}")
375
+
376
+ error_details = _extract_error_details(e)
377
+ if error_details:
378
+ click.echo(f" Details: {error_details}")
379
+
380
+ click.echo("💡 Please check your request and try again")
381
+
382
+
383
+ def _extract_error_details(e: ApiException) -> str:
384
+ """Extract meaningful error details from API exception response."""
385
+ if not (e.body or e.data):
386
+ return ""
387
+
388
+ try:
389
+ # Try to parse JSON error response
390
+ import json
391
+
392
+ error_data = json.loads(e.body) if e.body else e.data
393
+
394
+ # Common error message patterns
395
+ if isinstance(error_data, dict):
396
+ # Look for common error message fields
397
+ for field in ["message", "error", "detail", "description"]:
398
+ if field in error_data:
399
+ return str(error_data[field])
400
+
401
+ # Look for validation errors
402
+ if "errors" in error_data:
403
+ errors = error_data["errors"]
404
+ if isinstance(errors, list) and errors:
405
+ return f"Validation error: {errors[0]}"
406
+ elif isinstance(errors, dict):
407
+ # Format field-specific errors
408
+ error_msgs = []
409
+ for field, msgs in errors.items():
410
+ if isinstance(msgs, list):
411
+ error_msgs.append(f"{field}: {', '.join(msgs)}")
412
+ else:
413
+ error_msgs.append(f"{field}: {msgs}")
414
+ return "; ".join(error_msgs)
415
+
416
+ except JSONDecodeError:
417
+ # If we can't parse the error, fall back to raw response
418
+ pass
419
+
420
+ # Return first 200 chars of raw response as fallback
421
+ raw_response = str(e.body or e.data)
422
+ return raw_response[:200] + ("..." if len(raw_response) > 200 else "")
423
+
424
+
425
+ def _handle_network_error(
426
+ e: aiohttp.ClientConnectorError | aiohttp.ClientConnectionError,
427
+ ) -> None:
428
+ """Handle network connection errors."""
429
+ output_mode = _get_output_mode()
430
+
431
+ if output_mode == "json":
432
+ error_data = {
433
+ "status": "error",
434
+ "error": "Cannot connect to Workato API",
435
+ "error_code": "NETWORK_ERROR",
436
+ "details": str(e),
437
+ }
438
+ click.echo(json.dumps(error_data))
439
+ return
440
+
441
+ click.echo("❌ Cannot connect to Workato API")
442
+ click.echo(f" {str(e)}")
443
+ click.echo("💡 Please check:")
444
+ click.echo(" • Your internet connection is working")
445
+ click.echo(" • The Workato API is accessible")
446
+ click.echo(" • Your firewall/proxy settings allow the connection")
447
+
448
+
449
+ def _handle_timeout_error(e: TimeoutError) -> None:
450
+ """Handle timeout errors."""
451
+ output_mode = _get_output_mode()
452
+
453
+ if output_mode == "json":
454
+ error_data = {
455
+ "status": "error",
456
+ "error": "Request timed out",
457
+ "error_code": "TIMEOUT_ERROR",
458
+ }
459
+ click.echo(json.dumps(error_data))
460
+ return
461
+
462
+ click.echo("❌ Request timed out")
463
+ click.echo(" The request took too long to complete")
464
+ click.echo("💡 Please try:")
465
+ click.echo(" • Retry the operation")
466
+ click.echo(" • Check your network connection")
467
+ click.echo(" • The Workato API may be experiencing high load")
468
+
469
+
470
+ def _handle_server_disconnect_error(e: aiohttp.ServerDisconnectedError) -> None:
471
+ """Handle server disconnection errors."""
472
+ output_mode = _get_output_mode()
473
+
474
+ if output_mode == "json":
475
+ error_data = {
476
+ "status": "error",
477
+ "error": "Server disconnected unexpectedly",
478
+ "error_code": "SERVER_DISCONNECT",
479
+ }
480
+ click.echo(json.dumps(error_data))
481
+ return
482
+
483
+ click.echo("❌ Server disconnected")
484
+ click.echo(" The connection to Workato API was lost")
485
+ click.echo("💡 Please try:")
486
+ click.echo(" • Retry the operation")
487
+ click.echo(" • Check Workato status page for outages")
488
+
489
+
490
+ def _handle_ssl_error(e: aiohttp.ClientSSLError | ssl.SSLError) -> None:
491
+ """Handle SSL/certificate errors."""
492
+ output_mode = _get_output_mode()
493
+
494
+ if output_mode == "json":
495
+ error_data = {
496
+ "status": "error",
497
+ "error": "SSL certificate verification failed",
498
+ "error_code": "SSL_ERROR",
499
+ "details": str(e),
500
+ }
501
+ click.echo(json.dumps(error_data))
502
+ return
503
+
504
+ click.echo("❌ SSL certificate error")
505
+ click.echo(" Could not verify the SSL certificate")
506
+ click.echo(f" {str(e)}")
507
+ click.echo("💡 Please check:")
508
+ click.echo(" • Your system clock is set correctly")
509
+ click.echo(" • You have the latest CA certificates installed")
510
+ click.echo(" • Your network is not intercepting HTTPS connections")
511
+
512
+
513
+ def _handle_generic_cli_error(e: Exception) -> None:
514
+ """Handle any other unexpected CLI errors with a generic message."""
515
+ output_mode = _get_output_mode()
516
+
517
+ error_type = type(e).__name__
518
+ error_msg = str(e)
519
+
520
+ if output_mode == "json":
521
+ error_data = {
522
+ "status": "error",
523
+ "error": error_msg,
524
+ "error_code": "CLI_ERROR",
525
+ "error_type": error_type,
526
+ }
527
+ click.echo(json.dumps(error_data))
528
+ return
529
+
530
+ click.echo(f"❌ {error_type}")
531
+ click.echo(f" {error_msg}")
@@ -0,0 +1,32 @@
1
+ """Utilities for managing .gitignore files."""
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ def ensure_gitignore_entry(workspace_root: Path, entry: str) -> None:
7
+ """Ensure an entry is added to .gitignore in the workspace root.
8
+
9
+ Args:
10
+ workspace_root: Path to the workspace root directory
11
+ entry: Gitignore entry to add (e.g., "projects/*/workato/")
12
+ """
13
+ gitignore_file = workspace_root / ".gitignore"
14
+
15
+ # Read existing .gitignore if it exists
16
+ existing_lines = []
17
+ if gitignore_file.exists():
18
+ with open(gitignore_file) as f:
19
+ existing_lines = [line.rstrip("\n") for line in f.readlines()]
20
+
21
+ # Check if entry is already in .gitignore
22
+ if entry not in existing_lines:
23
+ # Add entry to .gitignore
24
+ with open(gitignore_file, "a") as f:
25
+ if existing_lines and existing_lines[-1] != "":
26
+ f.write("\n") # Add newline if file doesn't end with one
27
+ f.write(f"{entry}\n")
28
+
29
+
30
+ def ensure_stubs_in_gitignore(workspace_root: Path) -> None:
31
+ """Ensure workato/ generated packages are added to .gitignore in workspace root."""
32
+ ensure_gitignore_entry(workspace_root, "projects/*/workato/")
@@ -0,0 +1,44 @@
1
+ """Utility functions for handling .workato-ignore patterns"""
2
+
3
+ import fnmatch
4
+
5
+ from pathlib import Path
6
+
7
+
8
+ def load_ignore_patterns(workspace_root: Path) -> set[str]:
9
+ """Load patterns from .workato-ignore file"""
10
+ ignore_file = workspace_root / ".workato-ignore"
11
+ patterns = {".workatoenv"} # Always protect config file
12
+
13
+ if not ignore_file.exists():
14
+ return patterns
15
+
16
+ try:
17
+ with open(ignore_file, encoding="utf-8") as f:
18
+ for line in f:
19
+ line = line.strip()
20
+ if line and not line.startswith("#"):
21
+ patterns.add(line)
22
+ except (OSError, UnicodeDecodeError):
23
+ # If we can't read the ignore file, just use defaults
24
+ pass
25
+
26
+ return patterns
27
+
28
+
29
+ def should_skip_file(file_path: Path, ignore_patterns: set[str]) -> bool:
30
+ """Check if file should be skipped using .workato-ignore patterns"""
31
+ path_str = str(file_path)
32
+ file_name = file_path.name
33
+
34
+ for pattern in ignore_patterns:
35
+ # Check exact matches, glob patterns, and filename patterns
36
+ if (
37
+ fnmatch.fnmatch(path_str, pattern)
38
+ or fnmatch.fnmatch(file_name, pattern)
39
+ or path_str.startswith(pattern + "/")
40
+ or path_str.startswith(pattern + "\\")
41
+ ):
42
+ return True
43
+
44
+ return False
@@ -0,0 +1,63 @@
1
+ import sys
2
+ import threading
3
+ import time
4
+
5
+
6
+ class Spinner:
7
+ """Simple spinner animation with elapsed time counter for CLI"""
8
+
9
+ def __init__(self, message: str = "Loading") -> None:
10
+ self.message = message
11
+ self.spinner_chars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
12
+ self.running = False
13
+ self.thread: threading.Thread | None = None
14
+ self.start_time = 0.0
15
+ self._message_lock = threading.Lock() # Thread safety for message updates
16
+
17
+ def start(self) -> None:
18
+ self.running = True
19
+ self.start_time = time.time()
20
+ self.thread = threading.Thread(target=self._spin)
21
+ self.thread.daemon = True
22
+ self.thread.start()
23
+
24
+ def stop(self) -> float:
25
+ self.running = False
26
+ if self.thread:
27
+ self.thread.join()
28
+ # Clear the line
29
+ max_line_length = (
30
+ len(self.message) + 20
31
+ ) # Account for spinner, time, and padding
32
+ sys.stdout.write("\r" + " " * max_line_length + "\r")
33
+ sys.stdout.flush()
34
+
35
+ # Return elapsed time
36
+ if self.start_time:
37
+ return time.time() - self.start_time
38
+ return 0
39
+
40
+ def update_message(self, new_message: str) -> None:
41
+ """Update the spinner message while it's running"""
42
+ with self._message_lock:
43
+ self.message = new_message
44
+
45
+ def _spin(self) -> None:
46
+ idx = 0
47
+ while self.running:
48
+ char = self.spinner_chars[idx % len(self.spinner_chars)]
49
+ elapsed = time.time() - self.start_time
50
+ elapsed_str = f"{elapsed:.1f}s"
51
+
52
+ # Format: spinner + message + elapsed time
53
+ # Use lock to safely read the message
54
+ with self._message_lock:
55
+ current_message = self.message
56
+ display_text = f"{char} {current_message}... ({elapsed_str})"
57
+
58
+ # Use carriage return to overwrite the line
59
+ # This is simpler and more reliable than trying to clear with spaces
60
+ sys.stdout.write(f"\r{display_text}")
61
+ sys.stdout.flush()
62
+ time.sleep(0.1)
63
+ idx += 1