js7-client-python 2.0.1__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.
- js7/__init__.py +8 -0
- js7/api/joc/http/v_2_8_2/agent/cluster/confirm_node_loss.py +36 -0
- js7/api/joc/http/v_2_8_2/agent/cluster/switchover.py +36 -0
- js7/api/joc/http/v_2_8_2/agent/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/agents.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/cluster/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/cluster/deploy.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/cluster/revoke.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/cluster/store.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/export.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/import.py +74 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/deploy.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/revoke.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/store.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/subagents/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/subagents/disable.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/subagents/enable.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/subagents/reset.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/cluster/subagents/store.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/deploy.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/disable.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/enable.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/revoke.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/inventory/store.py +36 -0
- js7/api/joc/http/v_2_8_2/agents/reset.py +36 -0
- js7/api/joc/http/v_2_8_2/authentication/auth.py +32 -0
- js7/api/joc/http/v_2_8_2/authentication/joc_cockpit_permissions.py +28 -0
- js7/api/joc/http/v_2_8_2/authentication/login.py +35 -0
- js7/api/joc/http/v_2_8_2/authentication/logout.py +28 -0
- js7/api/joc/http/v_2_8_2/configuration/configuration.py +36 -0
- js7/api/joc/http/v_2_8_2/configuration/save.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/abort.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/abort_and_restart.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/cluster/appoint_nodes.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/cluster/confirm_node_loss.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/cluster/switchover.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/components.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/controller.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/register.py +42 -0
- js7/api/joc/http/v_2_8_2/controller/restart.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/terminate.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/test.py +36 -0
- js7/api/joc/http/v_2_8_2/controller/unregister.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/orders/cancel.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/orders/copy.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/orders/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/orders/generate.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/orders/modify.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/orders/orders.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/orders/submit.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/projections/calendar.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/projections/dates.py +36 -0
- js7/api/joc/http/v_2_8_2/daily_plan/projections/recreate.py +28 -0
- js7/api/joc/http/v_2_8_2/daily_plan/submissions/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/account/change_password.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/account/permissions.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/account/rename.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/account/store.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/accounts/accounts.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/accounts/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/accounts/disable.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/accounts/enable.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/accounts/reset_password.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/blocked_account/store.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/blocked_accounts/blocked_accounts.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/blocked_accounts/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/folder/folder.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/folder/rename.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/folders/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/folders/folders.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/folders/store.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/identity_service/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/identity_service/identity_service.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/identity_service/rename.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/identity_service/store.py +42 -0
- js7/api/joc/http/v_2_8_2/iam/identity_services/identity_services.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/permission/permission.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/permission/rename.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/permissions/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/permissions/permissions.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/permissions/store.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/role/rename.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/role/role.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/role/store.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/roles/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/iam/roles/roles.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/changes/changes.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/dependencies.py +33 -0
- js7/api/joc/http/v_2_8_2/inventory/deployment/deploy.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/deployment/import_deploy.py +66 -0
- js7/api/joc/http/v_2_8_2/inventory/deployment/revoke.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/export/export.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/export/folder.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/import_objects.py +88 -0
- js7/api/joc/http/v_2_8_2/inventory/releasables/recall/folder.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/releasables/recall/recall.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/release.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/remove/folder.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/remove/remove.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/add.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/checkout.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/clone.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/commit.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/credentials/add.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/credentials/credentials.py +28 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/credentials/remove.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/pull.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/git/push.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/read.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/store.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/repository/update.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/revalidate/folder.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/store.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/trash/delete/delete.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/trash/delete/folder.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/trash/restore.py +36 -0
- js7/api/joc/http/v_2_8_2/inventory/validate.py +42 -0
- js7/api/joc/http/v_2_8_2/joc/cluster/restart.py +36 -0
- js7/api/joc/http/v_2_8_2/joc/cluster/run.py +36 -0
- js7/api/joc/http/v_2_8_2/joc/cluster/switch_member.py +36 -0
- js7/api/joc/http/v_2_8_2/joc/license.py +28 -0
- js7/api/joc/http/v_2_8_2/joc/proxies/restart.py +36 -0
- js7/api/joc/http/v_2_8_2/joc/version.py +28 -0
- js7/api/joc/http/v_2_8_2/joc/versions.py +36 -0
- js7/api/joc/http/v_2_8_2/orders/add.py +33 -0
- js7/api/joc/http/v_2_8_2/orders/cancel.py +33 -0
- js7/api/joc/http/v_2_8_2/orders/confirm.py +36 -0
- js7/api/joc/http/v_2_8_2/orders/continue.py +36 -0
- js7/api/joc/http/v_2_8_2/orders/history.py +36 -0
- js7/api/joc/http/v_2_8_2/orders/orders.py +36 -0
- js7/api/joc/http/v_2_8_2/orders/remove_when_terminated.py +33 -0
- js7/api/joc/http/v_2_8_2/orders/resume.py +36 -0
- js7/api/joc/http/v_2_8_2/orders/suspend.py +36 -0
- js7/api/joc/http/v_2_8_2/settings/settings.py +28 -0
- js7/api/joc/http/v_2_8_2/settings/store.py +36 -0
- js7/api/joc/http/v_2_8_2/tasks/history.py +36 -0
- js7/api/joc/http/v_2_8_2/workflow/transition.py +36 -0
- js7/api/joc/http/v_2_8_2/workflow/workflow.py +36 -0
- js7/api/joc/http/v_2_8_2/workflows/resume.py +36 -0
- js7/api/joc/http/v_2_8_2/workflows/skip.py +36 -0
- js7/api/joc/http/v_2_8_2/workflows/stop.py +36 -0
- js7/api/joc/http/v_2_8_2/workflows/suspend.py +36 -0
- js7/api/joc/http/v_2_8_2/workflows/unskip.py +36 -0
- js7/api/joc/http/v_2_8_2/workflows/unstop.py +36 -0
- js7/api/joc/http/v_2_8_2/workflows/workflows.py +36 -0
- js7/api/joc/interface/dispatcher.py +76 -0
- js7/api/joc/interface/interface.py +8 -0
- js7/api/joc/interface/resolver.py +75 -0
- js7/client/action/agent/confirm_node_loss_agent_action.py +75 -0
- js7/client/action/agent/delete_subagent_action.py +77 -0
- js7/client/action/agent/delete_subagent_clusters_action.py +75 -0
- js7/client/action/agent/deploy_cluster_agents_action.py +75 -0
- js7/client/action/agent/deploy_standalone_agents_action.py +75 -0
- js7/client/action/agent/deploy_subagent_clusters_action.py +75 -0
- js7/client/action/agent/disable_standalone_agents_action.py +75 -0
- js7/client/action/agent/disable_subagents_action.py +75 -0
- js7/client/action/agent/enable_standalone_agents_action.py +75 -0
- js7/client/action/agent/enable_subagents_action.py +75 -0
- js7/client/action/agent/export_agents_action.py +101 -0
- js7/client/action/agent/get_agents_status_action.py +55 -0
- js7/client/action/agent/import_agents_action.py +139 -0
- js7/client/action/agent/remove_agent_action.py +79 -0
- js7/client/action/agent/reset_agents_action.py +79 -0
- js7/client/action/agent/reset_subagent_action.py +79 -0
- js7/client/action/agent/revoke_cluster_agents_action.py +75 -0
- js7/client/action/agent/revoke_standalone_agents_action.py +75 -0
- js7/client/action/agent/revoke_subagent_clusters_action.py +75 -0
- js7/client/action/agent/store_cluster_agents_action.py +99 -0
- js7/client/action/agent/store_standalone_agents_action.py +87 -0
- js7/client/action/agent/store_subagent_clusters_action.py +83 -0
- js7/client/action/agent/store_subagents_action.py +97 -0
- js7/client/action/agent/switchover_agent_action.py +75 -0
- js7/client/action/controller/appoint_nodes_controller_action.py +67 -0
- js7/client/action/controller/cancel_and_restart_controller_action.py +72 -0
- js7/client/action/controller/cancel_controller_action.py +71 -0
- js7/client/action/controller/confirm_cluster_node_loss_action.py +68 -0
- js7/client/action/controller/get_controller_components_action.py +41 -0
- js7/client/action/controller/get_controller_status_action.py +72 -0
- js7/client/action/controller/register_controller_action.py +93 -0
- js7/client/action/controller/restart_controller_action.py +72 -0
- js7/client/action/controller/switchover_controller_cluster_action.py +67 -0
- js7/client/action/controller/terminate_controller_action.py +72 -0
- js7/client/action/controller/test_controller_instance_action.py +65 -0
- js7/client/action/controller/unregister_controller_action.py +68 -0
- js7/client/action/daily_plan/cancel_orders_action.py +59 -0
- js7/client/action/daily_plan/copy_orders_action.py +108 -0
- js7/client/action/daily_plan/create_projections_action.py +24 -0
- js7/client/action/daily_plan/delete_orders_action.py +80 -0
- js7/client/action/daily_plan/delete_submissions_action.py +83 -0
- js7/client/action/daily_plan/generate_orders_action.py +121 -0
- js7/client/action/daily_plan/get_calendar_projections_action.py +59 -0
- js7/client/action/daily_plan/get_orders_action.py +70 -0
- js7/client/action/daily_plan/get_projection_dates_action.py +59 -0
- js7/client/action/daily_plan/modify_orders_action.py +130 -0
- js7/client/action/daily_plan/submit_orders_action.py +79 -0
- js7/client/action/helper/decrypt_action.py +82 -0
- js7/client/action/helper/encrypt_action.py +87 -0
- js7/client/action/iam/block_account_action.py +71 -0
- js7/client/action/iam/change_account_password_action.py +96 -0
- js7/client/action/iam/disable_accounts_action.py +75 -0
- js7/client/action/iam/enable_accounts_action.py +75 -0
- js7/client/action/iam/get_account_permissions_action.py +75 -0
- js7/client/action/iam/get_accounts_action.py +76 -0
- js7/client/action/iam/get_blocked_accounts_action.py +76 -0
- js7/client/action/iam/get_folder_permissions_action.py +112 -0
- js7/client/action/iam/get_identity_service_settings_action.py +70 -0
- js7/client/action/iam/get_identity_services_action.py +102 -0
- js7/client/action/iam/get_permissions_action.py +113 -0
- js7/client/action/iam/get_roles_action.py +57 -0
- js7/client/action/iam/remove_accounts_action.py +75 -0
- js7/client/action/iam/remove_folder_permissions_action.py +91 -0
- js7/client/action/iam/remove_identity_service_action.py +67 -0
- js7/client/action/iam/remove_permissions_action.py +88 -0
- js7/client/action/iam/remove_roles_action.py +75 -0
- js7/client/action/iam/rename_account_action.py +83 -0
- js7/client/action/iam/rename_folder_permissions_action.py +100 -0
- js7/client/action/iam/rename_identity_service_action.py +75 -0
- js7/client/action/iam/rename_permission_action.py +103 -0
- js7/client/action/iam/rename_role_action.py +83 -0
- js7/client/action/iam/reset_account_passwords_action.py +75 -0
- js7/client/action/iam/set_folder_permissions_action.py +96 -0
- js7/client/action/iam/set_permissions_action.py +91 -0
- js7/client/action/iam/store_account_action.py +69 -0
- js7/client/action/iam/store_identity_service_action.py +75 -0
- js7/client/action/iam/store_identity_service_settings_action.py +74 -0
- js7/client/action/iam/store_role_action.py +79 -0
- js7/client/action/iam/unblock_accounts_action.py +67 -0
- js7/client/action/inventory/deploy_configurations_action.py +138 -0
- js7/client/action/inventory/export_configurations_action.py +203 -0
- js7/client/action/inventory/export_folders_action.py +160 -0
- js7/client/action/inventory/get_change_dependencies_action.py +161 -0
- js7/client/action/inventory/get_changes_action.py +59 -0
- js7/client/action/inventory/get_git_credentials_action.py +29 -0
- js7/client/action/inventory/git_add_action.py +72 -0
- js7/client/action/inventory/git_checkout_action.py +88 -0
- js7/client/action/inventory/git_clone_action.py +84 -0
- js7/client/action/inventory/git_commit_action.py +80 -0
- js7/client/action/inventory/git_pull_action.py +72 -0
- js7/client/action/inventory/git_push_action.py +76 -0
- js7/client/action/inventory/import_configurations_action.py +153 -0
- js7/client/action/inventory/import_deploy_configurations_action.py +188 -0
- js7/client/action/inventory/read_from_local_repository_action.py +67 -0
- js7/client/action/inventory/recall_folder_action.py +89 -0
- js7/client/action/inventory/recall_released_configuration_action.py +75 -0
- js7/client/action/inventory/release_configuartions_action.py +89 -0
- js7/client/action/inventory/remove_configurations_action.py +77 -0
- js7/client/action/inventory/remove_configurations_from_trash_action.py +80 -0
- js7/client/action/inventory/remove_folder_action.py +67 -0
- js7/client/action/inventory/remove_folder_from_trash_action.py +69 -0
- js7/client/action/inventory/remove_git_credentials_action.py +53 -0
- js7/client/action/inventory/remove_repository_configuration_action.py +84 -0
- js7/client/action/inventory/restore_configuration_from_trash_action.py +84 -0
- js7/client/action/inventory/revalidate_folder_action.py +46 -0
- js7/client/action/inventory/revoke_configurations_action.py +86 -0
- js7/client/action/inventory/store_configuration_action.py +79 -0
- js7/client/action/inventory/store_git_credentials_action.py +99 -0
- js7/client/action/inventory/store_repository_configuration_action.py +124 -0
- js7/client/action/inventory/update_repository_configuration_action.py +85 -0
- js7/client/action/inventory/validate_configuration_action.py +58 -0
- js7/client/action/joc/get_components_versions_action.py +57 -0
- js7/client/action/joc/get_license_info_action.py +22 -0
- js7/client/action/joc/get_settings_action.py +19 -0
- js7/client/action/joc/get_version_action.py +17 -0
- js7/client/action/joc/restart_proxies_action.py +50 -0
- js7/client/action/joc/restart_service_action.py +74 -0
- js7/client/action/joc/run_service_action.py +74 -0
- js7/client/action/joc/store_settings_action.py +62 -0
- js7/client/action/joc/switch_over_action.py +89 -0
- js7/client/action/order/add_orders_action.py +115 -0
- js7/client/action/order/cancel_orders_action.py +93 -0
- js7/client/action/order/confirm_orders_action.py +86 -0
- js7/client/action/order/continue_orders_action.py +86 -0
- js7/client/action/order/get_order_history_action.py +84 -0
- js7/client/action/order/get_orders_action.py +83 -0
- js7/client/action/order/remove_terminated_orders_action.py +86 -0
- js7/client/action/order/resume_orders_action.py +102 -0
- js7/client/action/order/suspend_orders_action.py +97 -0
- js7/client/action/task/get_task_history_info_action.py +88 -0
- js7/client/action/workflow/get_workflow_versions_action.py +131 -0
- js7/client/action/workflow/resume_workflows_action.py +76 -0
- js7/client/action/workflow/set_workflow_version_as_current_action.py +86 -0
- js7/client/action/workflow/skip_job_instructions_action.py +83 -0
- js7/client/action/workflow/stop_job_instructions_action.py +83 -0
- js7/client/action/workflow/suspend_workflows_action.py +76 -0
- js7/client/action/workflow/unskip_job_instructions_action.py +83 -0
- js7/client/action/workflow/unstop_job_instructions_action.py +79 -0
- js7/client/auth/auth_provider.py +109 -0
- js7/client/auth/login.py +34 -0
- js7/client/auth/logout.py +22 -0
- js7/client/client.py +250 -0
- js7/client/context.py +40 -0
- js7/client/feature/agent/agent.py +26 -0
- js7/client/feature/agent/deploy.py +134 -0
- js7/client/feature/agent/manage.py +363 -0
- js7/client/feature/agent/operate.py +577 -0
- js7/client/feature/controller/controller.py +19 -0
- js7/client/feature/controller/manage.py +214 -0
- js7/client/feature/controller/operate.py +274 -0
- js7/client/feature/daily_plan/daily_plan.py +14 -0
- js7/client/feature/daily_plan/manage.py +179 -0
- js7/client/feature/daily_plan/operate.py +354 -0
- js7/client/feature/iam/iam.py +14 -0
- js7/client/feature/iam/manage.py +1311 -0
- js7/client/feature/inventory/inventory.py +13 -0
- js7/client/feature/inventory/manage.py +943 -0
- js7/client/feature/inventory/manage_repository.py +533 -0
- js7/client/feature/joc/joc.py +18 -0
- js7/client/feature/joc/manage.py +102 -0
- js7/client/feature/joc/operate.py +185 -0
- js7/client/feature/order/manage.py +79 -0
- js7/client/feature/order/operate.py +346 -0
- js7/client/feature/order/order.py +18 -0
- js7/client/feature/task/task.py +54 -0
- js7/client/feature/workflow/manage.py +54 -0
- js7/client/feature/workflow/operate.py +336 -0
- js7/client/feature/workflow/workflow.py +19 -0
- js7/java/lib/3rd-party/bcpg-jdk15to18-1.78.1.jar +0 -0
- js7/java/lib/3rd-party/bcpkix-jdk15to18-1.78.1.jar +0 -0
- js7/java/lib/3rd-party/bcprov-jdk15to18-1.78.1.jar +0 -0
- js7/java/lib/3rd-party/bcutil-jdk15to18-1.78.1.jar +0 -0
- js7/java/lib/3rd-party/commons-io-2.15.1.jar +0 -0
- js7/java/lib/3rd-party/commons-lang3-3.14.0.jar +0 -0
- js7/java/lib/3rd-party/jackson-core-2.14.2.jar +0 -0
- js7/java/lib/3rd-party/jackson-databind-2.14.2.jar +0 -0
- js7/java/lib/3rd-party/jackson-dataformat-xml-2.14.2.jar +0 -0
- js7/java/lib/3rd-party/jakarta.annotation-api-2.1.1.jar +0 -0
- js7/java/lib/3rd-party/javax.activation-api-1.2.0.jar +0 -0
- js7/java/lib/3rd-party/jaxb-api-2.4.0-b180830.0359.jar +0 -0
- js7/java/lib/3rd-party/org.apache.logging.log4j.log4j-api-2.23.1.jar +0 -0
- js7/java/lib/3rd-party/org.apache.logging.log4j.log4j-core-2.23.1.jar +0 -0
- js7/java/lib/3rd-party/org.apache.logging.log4j.log4j-slf4j2-impl-2.23.1.jar +0 -0
- js7/java/lib/3rd-party/org.slf4j.slf4j-api-2.0.13.jar +0 -0
- js7/java/lib/3rd-party/stax2-api-4.2.1.jar +0 -0
- js7/java/lib/3rd-party/woodstox-core-6.5.0.jar +0 -0
- js7/java/lib/sos/sos-commons-encryption-2.7.3.jar +0 -0
- js7/java/lib/sos/sos-commons-exception-2.7.3.jar +0 -0
- js7/java/lib/sos/sos-commons-sign-2.7.3.jar +0 -0
- js7/java/lib/sos/sos-webservices-json-2.7.3.jar +0 -0
- js7/model/__init__.py +132 -0
- js7/model/configuration/auth_configuration.py +24 -0
- js7/model/configuration/client_configuration.py +6 -0
- js7/model/configuration/http_configuration.py +10 -0
- js7/model/error/http/joc_exceptions.py +151 -0
- js7/model/private/api/endpoint.py +30 -0
- js7/model/private/http/joc/joc_v_2_8_2.py +3666 -0
- js7/model/public/client/common/__init__.py +27 -0
- js7/model/public/client/common/accounts.py +21 -0
- js7/model/public/client/common/audit_log.py +8 -0
- js7/model/public/client/common/changes.py +18 -0
- js7/model/public/client/common/configurations.py +53 -0
- js7/model/public/client/common/cycle.py +12 -0
- js7/model/public/client/common/git_credentials.py +12 -0
- js7/model/public/client/common/identity_service.py +14 -0
- js7/model/public/client/common/schedule_time.py +50 -0
- js7/model/public/client/common/store_agents.py +96 -0
- js7/model/public/client/enum/__init__.py +19 -0
- js7/model/public/client/enum/object_types.py +48 -0
- js7/model/public/client/enum/operation_type.py +11 -0
- js7/model/public/client/enum/order_priority.py +9 -0
- js7/model/public/client/filter/__init__.py +35 -0
- js7/model/public/client/filter/daily_plan_order_filters.py +255 -0
- js7/model/public/client/filter/element/__init__.py +3 -0
- js7/model/public/client/filter/element/folder.py +6 -0
- js7/model/public/client/filter/element/workflow_id.py +10 -0
- js7/model/public/client/filter/export_filter.py +52 -0
- js7/model/public/client/filter/export_folders_filter.py +34 -0
- js7/model/public/client/filter/get_order_filter.py +59 -0
- js7/model/public/client/filter/order_history_filter.py +113 -0
- js7/model/public/client/filter/resume_order_filter.py +45 -0
- js7/model/public/client/filter/suspend_order_filter.py +44 -0
- js7/model/public/client/filter/tasks_filter.py +50 -0
- js7/model/public/client/input/__init__.py +2 -0
- js7/model/public/client/input/add_order.py +50 -0
- js7/service/http_service.py +206 -0
- js7/util/bytes_converter/bytes_to_archive_bytes.py +52 -0
- js7/util/bytes_converter/bytes_to_file.py +12 -0
- js7/util/bytes_converter/files_to_bytes.py +52 -0
- js7/util/bytes_converter/read_bytes_archive_files_to_bytes.py +69 -0
- js7/util/bytes_converter/sign_to_bytes.py +106 -0
- js7/util/check_matching_version.py +20 -0
- js7/util/detect_archive_type.py +37 -0
- js7/util/str_converter/order_id_to_order_name.py +5 -0
- js7/validator/http/joc_http_status_validator.py +155 -0
- js7_client_python-2.0.1.0.dist-info/LICENSE +674 -0
- js7_client_python-2.0.1.0.dist-info/METADATA +763 -0
- js7_client_python-2.0.1.0.dist-info/RECORD +389 -0
- js7_client_python-2.0.1.0.dist-info/WHEEL +5 -0
- js7_client_python-2.0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Literal, Optional
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from .....model.public.client.filter.element.folder import Folder
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TasksFilter(BaseModel):
|
|
9
|
+
date_from: Optional[datetime] = None
|
|
10
|
+
"""Filters items starting from a date."""
|
|
11
|
+
|
|
12
|
+
date_to: Optional[datetime] = None
|
|
13
|
+
"""Filters items ending before a date."""
|
|
14
|
+
|
|
15
|
+
completed_date_from: Optional[datetime] = None
|
|
16
|
+
"""Filters items starting from a date."""
|
|
17
|
+
|
|
18
|
+
completed_date_to: Optional[datetime] = None
|
|
19
|
+
"""Filters items ending before a date."""
|
|
20
|
+
|
|
21
|
+
job_name: Optional[str] = None
|
|
22
|
+
"""
|
|
23
|
+
Limits result to a specified glob pattern of a Job name that supports '*' and '?' as wildcards where
|
|
24
|
+
- '*' : match zero or more characters
|
|
25
|
+
- '?' : match any single character
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
workflow_name: Optional[str] = None
|
|
29
|
+
"""
|
|
30
|
+
Limits result to a specified glob pattern of a Workflow name that supports '*' and '?' as wildcards where
|
|
31
|
+
- '*' : match zero or more characters
|
|
32
|
+
- '?' : match any single character
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
workflow_paths: Optional[str] = None
|
|
36
|
+
"""
|
|
37
|
+
Limits result to a specified glob pattern of a Workflow path that supports '*' and '?' as wildcards where
|
|
38
|
+
- '*' : match zero or more characters
|
|
39
|
+
- '?' : match any single character
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
folders: Optional[List[Folder]] = None
|
|
43
|
+
"""Limits the result to a collection of folders."""
|
|
44
|
+
|
|
45
|
+
history_states: Optional[List[Literal["FAILED", "INCOMPLETE", "SUCCESSFUL"]]] = None
|
|
46
|
+
"""Limits result to specified states."""
|
|
47
|
+
|
|
48
|
+
criticalities: Optional[List[Literal["CRITICAL", "NORMAL"]]] = None
|
|
49
|
+
"""Possible values are NORMAL and NORMAL Only records with these criticalities are responsed."""
|
|
50
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional, Union
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
|
|
4
|
+
from .....model.public.client.common.schedule_time import ScheduleTime
|
|
5
|
+
|
|
6
|
+
from ..enum.order_priority import OrderPriority
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PlanID(BaseModel):
|
|
10
|
+
notice_space_key: str
|
|
11
|
+
"""Schema ID of a plan, e.g. 'DailyPlan'."""
|
|
12
|
+
|
|
13
|
+
plan_schema_id: str
|
|
14
|
+
"""Plan key of the plan."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Order(BaseModel):
|
|
18
|
+
arguments: Optional[Dict[str, Any]] = None
|
|
19
|
+
"""An object with key-value pairs. The value can be a string, number or boolean."""
|
|
20
|
+
|
|
21
|
+
block_position: Optional[Union[List[Union[int, str]], str]] = None
|
|
22
|
+
"""The order runs only inside the specified block instruction. The position can also be specified by the label of the block instruction."""
|
|
23
|
+
|
|
24
|
+
end_positions: Optional[List[Union[List[Union[int, str]], str]]] = None
|
|
25
|
+
"""The order ends on one of these positions. The position can also be specified by the label of the instruction."""
|
|
26
|
+
|
|
27
|
+
force_job_admission: bool = False
|
|
28
|
+
"""If true then any admission times at a Job instruction will be ignored."""
|
|
29
|
+
|
|
30
|
+
open_closed_plan: bool = False
|
|
31
|
+
"""If true then a closed plan will be reopen automatically."""
|
|
32
|
+
|
|
33
|
+
order_name: Optional[str]
|
|
34
|
+
"""`order_name` is only a part of the `order_id` in the form "#<date>#T<uniqueId>-<orderName>"."""
|
|
35
|
+
|
|
36
|
+
plan_id: Optional[PlanID] = None
|
|
37
|
+
|
|
38
|
+
priority: Optional[Union[OrderPriority, int]] = None
|
|
39
|
+
"""Priority of the order."""
|
|
40
|
+
|
|
41
|
+
scheduled_for: Optional[ScheduleTime] = None
|
|
42
|
+
"""Sets the start time of the order execution."""
|
|
43
|
+
|
|
44
|
+
start_position: Optional[Union[List[Union[int, str]], str]] = None
|
|
45
|
+
"""The order starts with the first instruction per default. The position can also be specified by the label of the instruction."""
|
|
46
|
+
|
|
47
|
+
tags: Optional[List[str]] = None
|
|
48
|
+
|
|
49
|
+
workflow_path: str
|
|
50
|
+
"""e. g. `/folder/my-workflow`"""
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import gzip
|
|
2
|
+
import mimetypes
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import http.client, ssl, time
|
|
5
|
+
from typing import Callable, Optional, Union, Dict
|
|
6
|
+
from ssl import SSLContext
|
|
7
|
+
import uuid
|
|
8
|
+
|
|
9
|
+
from ..model.configuration.http_configuration import HTTPConfiguration
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HTTPService:
|
|
13
|
+
"""
|
|
14
|
+
Functionality:
|
|
15
|
+
- Uses `time.monotonic` to obtain a monotonically increasing time source and
|
|
16
|
+
avoid issues caused by system time changes (e.g. daylight saving time)
|
|
17
|
+
- Closes the connection to the server after 30 seconds of inactivity and reopen the connection after the next use again.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
IDLE_TIMEOUT = 30 # Reconnects after 30 seconds of inactivity
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
*,
|
|
25
|
+
configuration: HTTPConfiguration,
|
|
26
|
+
response_validator: Optional[Callable[[str, int, bytes], None]] = None
|
|
27
|
+
):
|
|
28
|
+
|
|
29
|
+
self._config = configuration
|
|
30
|
+
self._response_validator = response_validator
|
|
31
|
+
|
|
32
|
+
self._conn: Union[http.client.HTTPSConnection, http.client.HTTPConnection, None] = None
|
|
33
|
+
self._https_ctx: Optional[SSLContext] = None
|
|
34
|
+
self._last_used: float = 0.0
|
|
35
|
+
|
|
36
|
+
# These variables can be modified at runtime by other methods to adjust the SSL context
|
|
37
|
+
self.auth_certfile_path: Optional[Union[Path, str]] = None
|
|
38
|
+
self.auth_keyfile_path: Optional[Union[Path, str]] = None
|
|
39
|
+
|
|
40
|
+
def __enter__(self):
|
|
41
|
+
self._ensure_connection()
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore
|
|
45
|
+
if exc_type is not None:
|
|
46
|
+
self.close()
|
|
47
|
+
|
|
48
|
+
def _ensure_connection(self):
|
|
49
|
+
now = time.monotonic()
|
|
50
|
+
if self._conn is None or (now - self._last_used) > self.IDLE_TIMEOUT:
|
|
51
|
+
self._open_connection()
|
|
52
|
+
|
|
53
|
+
# Creates the context for the ssl connection
|
|
54
|
+
def _create_ssl_context(self) -> SSLContext:
|
|
55
|
+
if self._https_ctx:
|
|
56
|
+
return self._https_ctx
|
|
57
|
+
|
|
58
|
+
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
|
59
|
+
|
|
60
|
+
# Sets fixed tls version
|
|
61
|
+
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
62
|
+
|
|
63
|
+
# Optional: Server Certificate
|
|
64
|
+
if self._config.cafile_path:
|
|
65
|
+
ctx.load_verify_locations(cafile=self._config.cafile_path)
|
|
66
|
+
|
|
67
|
+
# Optional: Client certificate
|
|
68
|
+
if self.auth_certfile_path and not self.auth_keyfile_path:
|
|
69
|
+
raise ValueError("client certificate provided without private key")
|
|
70
|
+
|
|
71
|
+
if self.auth_certfile_path and self.auth_keyfile_path:
|
|
72
|
+
ctx.load_cert_chain(
|
|
73
|
+
certfile=self.auth_certfile_path,
|
|
74
|
+
keyfile=self.auth_keyfile_path
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Ensures security
|
|
78
|
+
ctx.verify_mode = ssl.CERT_REQUIRED
|
|
79
|
+
ctx.check_hostname = True
|
|
80
|
+
self._https_ctx = ctx
|
|
81
|
+
return self._https_ctx
|
|
82
|
+
|
|
83
|
+
def _open_connection(self):
|
|
84
|
+
self.close()
|
|
85
|
+
|
|
86
|
+
if self._config.ssl:
|
|
87
|
+
self._conn = http.client.HTTPSConnection(
|
|
88
|
+
host=self._config.host,
|
|
89
|
+
port=self._config.port,
|
|
90
|
+
timeout=30,
|
|
91
|
+
context=self._create_ssl_context()
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
self._conn = http.client.HTTPConnection(
|
|
95
|
+
host=self._config.host,
|
|
96
|
+
port=self._config.port,
|
|
97
|
+
timeout=30
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self._last_used = time.monotonic()
|
|
101
|
+
|
|
102
|
+
def _request(self, method: str, path: str, headers: Dict[str, str], body: Optional[str]) -> http.client.HTTPResponse:
|
|
103
|
+
self._ensure_connection()
|
|
104
|
+
|
|
105
|
+
conn = self._conn
|
|
106
|
+
if conn is None:
|
|
107
|
+
raise RuntimeError("Connection not initialized.")
|
|
108
|
+
|
|
109
|
+
response: Optional[http.client.HTTPResponse] = None
|
|
110
|
+
|
|
111
|
+
# Reconnects and retries the request once on connection failure
|
|
112
|
+
try:
|
|
113
|
+
conn.request(method=method, url=path, body=body, headers=headers)
|
|
114
|
+
response = conn.getresponse()
|
|
115
|
+
except (BrokenPipeError, ConnectionResetError, TimeoutError):
|
|
116
|
+
self.close()
|
|
117
|
+
|
|
118
|
+
time.sleep(3) # Waits for 3 seconds
|
|
119
|
+
|
|
120
|
+
self._open_connection()
|
|
121
|
+
conn = self._conn
|
|
122
|
+
if conn is None:
|
|
123
|
+
raise RuntimeError("Connection not initialized after reconnect.")
|
|
124
|
+
conn.request(method=method, url=path, body=body, headers=headers)
|
|
125
|
+
response = conn.getresponse()
|
|
126
|
+
|
|
127
|
+
return response
|
|
128
|
+
|
|
129
|
+
def _decode_response(self, path:str, response: http.client.HTTPResponse) -> bytes:
|
|
130
|
+
raw_bytes = response.read()
|
|
131
|
+
|
|
132
|
+
# Raises a error for the given statuscode
|
|
133
|
+
if self._response_validator:
|
|
134
|
+
self._response_validator(path, response.status, raw_bytes)
|
|
135
|
+
|
|
136
|
+
if "gzip" in (response.headers.get("Content-Encoding", "")).lower():
|
|
137
|
+
return gzip.decompress(raw_bytes)
|
|
138
|
+
|
|
139
|
+
return raw_bytes
|
|
140
|
+
|
|
141
|
+
# (i) close method
|
|
142
|
+
def close(self):
|
|
143
|
+
if self._conn:
|
|
144
|
+
self._conn.close()
|
|
145
|
+
self._conn = None
|
|
146
|
+
self._https_ctx = None
|
|
147
|
+
|
|
148
|
+
# (i) POST method
|
|
149
|
+
def post(self, path: str, body: Optional[str], headers: Dict[str, str]) -> bytes:
|
|
150
|
+
response = self._request("POST", path, headers, body)
|
|
151
|
+
return self._decode_response(path, response)
|
|
152
|
+
|
|
153
|
+
# (i) GET method
|
|
154
|
+
def get(self, path: str, headers: Dict[str, str]) -> bytes:
|
|
155
|
+
response = self._request(method="GET", path=path, headers=headers, body=None)
|
|
156
|
+
return self._decode_response(path, response)
|
|
157
|
+
|
|
158
|
+
# (i) Upload file
|
|
159
|
+
def upload_file(self, path: str, access_token: str, file: bytes, filename: str, form_fields: Optional[Dict[str, str]] = None) -> bytes:
|
|
160
|
+
self._ensure_connection()
|
|
161
|
+
conn = self._conn
|
|
162
|
+
if conn is None:
|
|
163
|
+
raise RuntimeError("Connection not initialized.")
|
|
164
|
+
|
|
165
|
+
boundary = "----Boundary" + uuid.uuid4().hex
|
|
166
|
+
content_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
|
167
|
+
|
|
168
|
+
body = bytearray()
|
|
169
|
+
|
|
170
|
+
def add_field(name: str, value: str) -> None:
|
|
171
|
+
body.extend(f"--{boundary}\r\n".encode())
|
|
172
|
+
body.extend(
|
|
173
|
+
f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode()
|
|
174
|
+
)
|
|
175
|
+
body.extend(value.encode("utf-8"))
|
|
176
|
+
body.extend(b"\r\n")
|
|
177
|
+
|
|
178
|
+
# optional form fields first
|
|
179
|
+
if form_fields:
|
|
180
|
+
for k, v in form_fields.items():
|
|
181
|
+
add_field(k, v)
|
|
182
|
+
|
|
183
|
+
# file part
|
|
184
|
+
body.extend(f"--{boundary}\r\n".encode())
|
|
185
|
+
body.extend(
|
|
186
|
+
(
|
|
187
|
+
f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n'
|
|
188
|
+
f"Content-Type: {content_type}\r\n\r\n"
|
|
189
|
+
).encode()
|
|
190
|
+
)
|
|
191
|
+
body.extend(file)
|
|
192
|
+
body.extend(b"\r\n")
|
|
193
|
+
|
|
194
|
+
# closing boundary
|
|
195
|
+
body.extend(f"--{boundary}--\r\n".encode())
|
|
196
|
+
|
|
197
|
+
headers = {
|
|
198
|
+
"Content-Type": f"multipart/form-data; boundary={boundary}",
|
|
199
|
+
"Content-Length": str(len(body)),
|
|
200
|
+
"X-Access-Token": access_token,
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
conn.request("POST", path, body=body, headers=headers)
|
|
204
|
+
response = conn.getresponse()
|
|
205
|
+
|
|
206
|
+
return self._decode_response(path=path, response=response)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import List, Literal, Tuple
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
import zipfile
|
|
4
|
+
import tarfile
|
|
5
|
+
import posixpath
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def bytes_to_archive_bytes(
|
|
9
|
+
*,
|
|
10
|
+
archive_format: Literal["ZIP", "TAR_GZ"],
|
|
11
|
+
files: List[Tuple[str, bytes]], # path, content bytes
|
|
12
|
+
) -> bytes:
|
|
13
|
+
|
|
14
|
+
# Validate: archive_format
|
|
15
|
+
if archive_format not in ("ZIP", "TAR_GZ"):
|
|
16
|
+
raise ValueError("'archive_format' must be 'ZIP' or 'TAR_GZ'.")
|
|
17
|
+
|
|
18
|
+
# Validate: files
|
|
19
|
+
if not files:
|
|
20
|
+
raise ValueError("'files' is required.")
|
|
21
|
+
|
|
22
|
+
for path, content in files:
|
|
23
|
+
if not path:
|
|
24
|
+
raise ValueError("File path must not be empty.")
|
|
25
|
+
|
|
26
|
+
if not content:
|
|
27
|
+
raise ValueError("File content must not be empty.")
|
|
28
|
+
|
|
29
|
+
# Normalize paths (no leading slash, POSIX-style)
|
|
30
|
+
normalized_files = [
|
|
31
|
+
(posixpath.normpath(path.lstrip("/")), bytes(content))
|
|
32
|
+
for path, content in files
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
buffer = BytesIO()
|
|
36
|
+
|
|
37
|
+
# Build: ZIP
|
|
38
|
+
if archive_format == "ZIP":
|
|
39
|
+
with zipfile.ZipFile(buffer, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
40
|
+
for path, content in normalized_files:
|
|
41
|
+
zf.writestr(path, content)
|
|
42
|
+
|
|
43
|
+
# Build: TAR.GZ
|
|
44
|
+
elif archive_format == "TAR_GZ":
|
|
45
|
+
with tarfile.open(fileobj=buffer, mode="w:gz") as tf:
|
|
46
|
+
for path, content in normalized_files:
|
|
47
|
+
info = tarfile.TarInfo(name=path)
|
|
48
|
+
info.size = len(content)
|
|
49
|
+
tf.addfile(info, BytesIO(content))
|
|
50
|
+
|
|
51
|
+
buffer.seek(0)
|
|
52
|
+
return buffer.read()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def bytes_to_file(*, data: bytes, out_path: Path) -> bool:
|
|
5
|
+
"""out_path: /my-file.zip"""
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
9
|
+
out_path.write_bytes(data)
|
|
10
|
+
return True
|
|
11
|
+
except OSError:
|
|
12
|
+
return False
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def files_to_bytes(
|
|
6
|
+
*,
|
|
7
|
+
file_path: Path,
|
|
8
|
+
filter_suffixes: Optional[List[str]] = None,
|
|
9
|
+
) -> List[Tuple[str, bytes]]:
|
|
10
|
+
"""
|
|
11
|
+
Reads a file or directory into memory.
|
|
12
|
+
|
|
13
|
+
Input:
|
|
14
|
+
- file: File or directory to read from.
|
|
15
|
+
- filter_suffixes: If set, only files with one of these suffixes (e.g. [".json", ".yaml"]) are included.
|
|
16
|
+
|
|
17
|
+
Result:
|
|
18
|
+
- List of (relative_posix_path, file_content_bytes).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
if not file_path.exists():
|
|
22
|
+
raise ValueError(f"File path does not exist: {file_path}")
|
|
23
|
+
|
|
24
|
+
# Normalize filter suffixes
|
|
25
|
+
suffixes: Optional[Tuple[str, ...]] = (
|
|
26
|
+
tuple(filter_suffixes) if filter_suffixes else None
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
result: List[Tuple[str, bytes]] = []
|
|
30
|
+
|
|
31
|
+
def is_allowed(path: Path) -> bool:
|
|
32
|
+
return suffixes is None or path.name.endswith(suffixes)
|
|
33
|
+
|
|
34
|
+
if file_path.is_dir():
|
|
35
|
+
for p in file_path.rglob("*"):
|
|
36
|
+
if not p.is_file():
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
if not is_allowed(p):
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
relative_path = p.relative_to(file_path).as_posix()
|
|
43
|
+
result.append((relative_path, p.read_bytes()))
|
|
44
|
+
|
|
45
|
+
elif file_path.is_file():
|
|
46
|
+
if is_allowed(file_path):
|
|
47
|
+
result.append((file_path.name, file_path.read_bytes()))
|
|
48
|
+
|
|
49
|
+
else:
|
|
50
|
+
raise ValueError(f"Invalid file input: {file_path}")
|
|
51
|
+
|
|
52
|
+
return result
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import List, Optional, Tuple
|
|
2
|
+
import zipfile
|
|
3
|
+
import tarfile
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
|
|
6
|
+
from ..detect_archive_type import detect_archive_type
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def read_bytes_archive_files_to_bytes(
|
|
10
|
+
file: bytes,
|
|
11
|
+
filter_suffixes: Optional[List[str]] = None
|
|
12
|
+
) -> Optional[List[Tuple[str, bytes]]]:
|
|
13
|
+
"""Returns POSIX path and file as bytes or None if not an archive or no files found."""
|
|
14
|
+
|
|
15
|
+
if not file:
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
archive_type = detect_archive_type(file)
|
|
19
|
+
if archive_type is None:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
# Normalize suffix filter
|
|
23
|
+
suffixes = None
|
|
24
|
+
if filter_suffixes:
|
|
25
|
+
suffixes = tuple(s.lower() for s in filter_suffixes)
|
|
26
|
+
|
|
27
|
+
result: List[Tuple[str, bytes]] = []
|
|
28
|
+
|
|
29
|
+
# ZIP
|
|
30
|
+
if archive_type == "ZIP":
|
|
31
|
+
try:
|
|
32
|
+
with zipfile.ZipFile(BytesIO(file), "r") as zf:
|
|
33
|
+
for info in zf.infolist():
|
|
34
|
+
if info.is_dir():
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
posix_path = info.filename.replace("\\", "/")
|
|
38
|
+
|
|
39
|
+
if suffixes and not posix_path.lower().endswith(suffixes):
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
data = zf.read(info)
|
|
43
|
+
result.append((posix_path, data))
|
|
44
|
+
except (zipfile.BadZipFile, OSError):
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
# TAR.GZ
|
|
48
|
+
elif archive_type == "TAR_GZ":
|
|
49
|
+
try:
|
|
50
|
+
with tarfile.open(fileobj=BytesIO(file), mode="r:gz") as tf:
|
|
51
|
+
for member in tf.getmembers():
|
|
52
|
+
if not member.isfile():
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
posix_path = member.name.replace("\\", "/")
|
|
56
|
+
|
|
57
|
+
if suffixes and not posix_path.lower().endswith(suffixes):
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
extracted = tf.extractfile(member)
|
|
61
|
+
if extracted is None:
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
data = extracted.read()
|
|
65
|
+
result.append((posix_path, data))
|
|
66
|
+
except (tarfile.TarError, OSError):
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
return result or None
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Optional, Tuple, Union
|
|
3
|
+
import base64
|
|
4
|
+
|
|
5
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
6
|
+
from cryptography.hazmat.primitives.asymmetric import padding, rsa, ec
|
|
7
|
+
from cryptography.exceptions import InvalidSignature
|
|
8
|
+
from cryptography.hazmat.primitives.asymmetric.types import PublicKeyTypes
|
|
9
|
+
|
|
10
|
+
from cryptography.hazmat.backends import default_backend
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _verify_signature(
|
|
14
|
+
*,
|
|
15
|
+
data: bytes,
|
|
16
|
+
signature: bytes,
|
|
17
|
+
public_key: Union[rsa.RSAPublicKey, ec.EllipticCurvePublicKey],
|
|
18
|
+
hash_alg: hashes.HashAlgorithm,
|
|
19
|
+
) -> bool:
|
|
20
|
+
try:
|
|
21
|
+
if isinstance(public_key, rsa.RSAPublicKey):
|
|
22
|
+
public_key.verify(
|
|
23
|
+
signature,
|
|
24
|
+
data,
|
|
25
|
+
padding.PKCS1v15(),
|
|
26
|
+
hash_alg,
|
|
27
|
+
)
|
|
28
|
+
else:
|
|
29
|
+
public_key.verify(
|
|
30
|
+
signature,
|
|
31
|
+
data,
|
|
32
|
+
ec.ECDSA(hash_alg),
|
|
33
|
+
)
|
|
34
|
+
return True
|
|
35
|
+
except InvalidSignature:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
def sign_to_bytes(
|
|
39
|
+
*,
|
|
40
|
+
files: List[Tuple[str, bytes]],
|
|
41
|
+
private_key_file: Path,
|
|
42
|
+
public_key_file: Optional[Path],
|
|
43
|
+
key_password: Optional[bytes],
|
|
44
|
+
hash_alg: hashes.HashAlgorithm
|
|
45
|
+
) -> List[Tuple[str, bytes]]:
|
|
46
|
+
|
|
47
|
+
if not private_key_file or not hash_alg:
|
|
48
|
+
raise ValueError("'private_key_file' and 'hash_alg' is required")
|
|
49
|
+
|
|
50
|
+
# ---- load private key ----
|
|
51
|
+
with open(private_key_file, "rb") as f:
|
|
52
|
+
private_key = serialization.load_pem_private_key(
|
|
53
|
+
f.read(),
|
|
54
|
+
password=key_password,
|
|
55
|
+
backend=default_backend(),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if not isinstance(private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)):
|
|
59
|
+
raise TypeError("Only RSA and ECDSA private keys are supported")
|
|
60
|
+
|
|
61
|
+
# ---- load public key ----
|
|
62
|
+
public_key: Optional[PublicKeyTypes] = None
|
|
63
|
+
|
|
64
|
+
if public_key_file:
|
|
65
|
+
with open(public_key_file, "rb") as f:
|
|
66
|
+
public_key = serialization.load_pem_public_key(
|
|
67
|
+
f.read(),
|
|
68
|
+
backend=default_backend(),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if not isinstance(public_key, (rsa.RSAPublicKey, ec.EllipticCurvePublicKey)):
|
|
72
|
+
raise TypeError("Unsupported public key type.")
|
|
73
|
+
|
|
74
|
+
# ---- sign ----
|
|
75
|
+
result: List[Tuple[str, bytes]] = []
|
|
76
|
+
|
|
77
|
+
for path, data in files:
|
|
78
|
+
if isinstance(private_key, rsa.RSAPrivateKey):
|
|
79
|
+
signature = private_key.sign(
|
|
80
|
+
data,
|
|
81
|
+
padding.PKCS1v15(),
|
|
82
|
+
hash_alg,
|
|
83
|
+
)
|
|
84
|
+
else:
|
|
85
|
+
signature = private_key.sign(
|
|
86
|
+
data,
|
|
87
|
+
ec.ECDSA(hash_alg),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
signature_b64_bytes = base64.b64encode(signature)
|
|
91
|
+
|
|
92
|
+
sig_path = f"{path}.sig"
|
|
93
|
+
|
|
94
|
+
# ---- optional verification ----
|
|
95
|
+
if public_key:
|
|
96
|
+
if not _verify_signature(
|
|
97
|
+
data=data,
|
|
98
|
+
signature=signature,
|
|
99
|
+
public_key=public_key,
|
|
100
|
+
hash_alg=hash_alg,
|
|
101
|
+
):
|
|
102
|
+
raise ValueError(f"Signature verification failed for '{sig_path}'")
|
|
103
|
+
|
|
104
|
+
result.append((sig_path, signature_b64_bytes))
|
|
105
|
+
|
|
106
|
+
return result
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import Tuple
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _version_to_tuple(input: str) -> Tuple[int, int, int]:
|
|
5
|
+
try:
|
|
6
|
+
ep_major, ep_minor, ep_patch = input.split(".")
|
|
7
|
+
return int(ep_major), int(ep_minor), int(ep_patch)
|
|
8
|
+
except Exception as e:
|
|
9
|
+
raise ValueError(f"Invalid version string '{input}'. Error: {e}")
|
|
10
|
+
|
|
11
|
+
def check_matching_version(*, min: str, max: str, check: str) -> bool:
|
|
12
|
+
# Normalize to MAJOR.MINOR.PATCH
|
|
13
|
+
check = check.split("-", 1)[0]
|
|
14
|
+
check = ".".join(check.split(".")[:3])
|
|
15
|
+
|
|
16
|
+
min_version: Tuple[int, int, int] = _version_to_tuple(min)
|
|
17
|
+
max_version: Tuple[int, int, int] = _version_to_tuple(max)
|
|
18
|
+
check_version: Tuple[int, int, int] = _version_to_tuple(check)
|
|
19
|
+
|
|
20
|
+
return min_version <= check_version <= max_version
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Literal, Optional, Union
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _detect_from_bytes(data: bytes) -> Optional[Literal["ZIP", "TAR_GZ"]]:
|
|
6
|
+
if len(data) < 4:
|
|
7
|
+
return None
|
|
8
|
+
|
|
9
|
+
# ZIP
|
|
10
|
+
if data.startswith((b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08")):
|
|
11
|
+
return "ZIP"
|
|
12
|
+
|
|
13
|
+
# GZIP (tar.gz)
|
|
14
|
+
if data.startswith(b"\x1f\x8b"):
|
|
15
|
+
return "TAR_GZ"
|
|
16
|
+
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def detect_archive_type(file: Union[bytes, Path]) -> Optional[Literal["ZIP", "TAR_GZ"]]:
|
|
21
|
+
"""Detect archive type from raw bytes or file path using magic bytes."""
|
|
22
|
+
|
|
23
|
+
if isinstance(file, bytes):
|
|
24
|
+
return _detect_from_bytes(file)
|
|
25
|
+
|
|
26
|
+
if isinstance(file, Path):
|
|
27
|
+
if not file.exists() or not file.is_file():
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
with file.open("rb") as f:
|
|
32
|
+
head = f.read(4)
|
|
33
|
+
return _detect_from_bytes(head)
|
|
34
|
+
except OSError:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
return None
|