From 60b8b5692ac71f0963cec05640187ece9db48e76 Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Tue, 9 Sep 2025 07:01:00 -0700 Subject: [PATCH] feat: Retry requests for network errors in Swift (box/box-codegen#820) --- .codegen.json | 1 + box_sdk_gen/__init__.py | 15 + box_sdk_gen/box/__init__.py | 13 + box_sdk_gen/box/ccg_auth.py | 236 +++++ box_sdk_gen/box/developer_token_auth.py | 139 +++ box_sdk_gen/box/errors.py | 136 +++ box_sdk_gen/box/event_stream.py | 236 +++++ box_sdk_gen/box/jwt_auth.py | 478 +++++++++ box_sdk_gen/box/oauth.py | 250 +++++ box_sdk_gen/box/token_storage.py | 74 ++ box_sdk_gen/client.py | 535 ++++++++++ box_sdk_gen/internal/__init__.py | 9 + box_sdk_gen/internal/base_object.py | 165 +++ box_sdk_gen/internal/errors.py | 6 + box_sdk_gen/internal/logging.py | 31 + box_sdk_gen/internal/null_value.py | 5 + box_sdk_gen/internal/utils.py | 501 +++++++++ box_sdk_gen/managers/__init__.py | 159 +++ box_sdk_gen/managers/ai.py | 482 +++++++++ box_sdk_gen/managers/ai_studio.py | 328 ++++++ box_sdk_gen/managers/app_item_associations.py | 197 ++++ box_sdk_gen/managers/archives.py | 180 ++++ box_sdk_gen/managers/authorization.py | 434 ++++++++ box_sdk_gen/managers/avatars.py | 174 ++++ box_sdk_gen/managers/chunked_uploads.py | 872 ++++++++++++++++ box_sdk_gen/managers/classifications.py | 707 +++++++++++++ .../collaboration_allowlist_entries.py | 226 ++++ .../collaboration_allowlist_exempt_targets.py | 227 ++++ box_sdk_gen/managers/collections.py | 217 ++++ box_sdk_gen/managers/comments.py | 357 +++++++ box_sdk_gen/managers/device_pinners.py | 188 ++++ box_sdk_gen/managers/docgen.py | 297 ++++++ box_sdk_gen/managers/docgen_template.py | 349 +++++++ box_sdk_gen/managers/downloads.py | 256 +++++ box_sdk_gen/managers/email_aliases.py | 175 ++++ box_sdk_gen/managers/events.py | 641 ++++++++++++ box_sdk_gen/managers/external_users.py | 106 ++ box_sdk_gen/managers/file_classifications.py | 313 ++++++ box_sdk_gen/managers/file_metadata.py | 420 ++++++++ box_sdk_gen/managers/file_requests.py | 398 ++++++++ .../managers/file_version_legal_holds.py | 179 ++++ .../managers/file_version_retentions.py | 187 ++++ box_sdk_gen/managers/file_versions.py | 423 ++++++++ box_sdk_gen/managers/file_watermarks.py | 199 ++++ box_sdk_gen/managers/files.py | 786 ++++++++++++++ .../managers/folder_classifications.py | 331 ++++++ box_sdk_gen/managers/folder_locks.py | 216 ++++ box_sdk_gen/managers/folder_metadata.py | 450 ++++++++ box_sdk_gen/managers/folder_watermarks.py | 214 ++++ box_sdk_gen/managers/folders.py | 966 ++++++++++++++++++ box_sdk_gen/managers/groups.py | 450 ++++++++ box_sdk_gen/managers/hub_collaborations.py | 360 +++++++ box_sdk_gen/managers/hub_items.py | 177 ++++ box_sdk_gen/managers/hubs.py | 475 +++++++++ box_sdk_gen/managers/integration_mappings.py | 504 +++++++++ box_sdk_gen/managers/invites.py | 185 ++++ box_sdk_gen/managers/legal_hold_policies.py | 328 ++++++ .../managers/legal_hold_policy_assignments.py | 393 +++++++ box_sdk_gen/managers/list_collaborations.py | 329 ++++++ box_sdk_gen/managers/memberships.py | 444 ++++++++ .../managers/metadata_cascade_policies.py | 333 ++++++ box_sdk_gen/managers/metadata_templates.py | 636 ++++++++++++ box_sdk_gen/managers/recent_items.py | 109 ++ box_sdk_gen/managers/retention_policies.py | 443 ++++++++ .../managers/retention_policy_assignments.py | 371 +++++++ box_sdk_gen/managers/search.py | 558 ++++++++++ box_sdk_gen/managers/session_termination.py | 139 +++ .../managers/shared_links_app_items.py | 84 ++ box_sdk_gen/managers/shared_links_files.py | 540 ++++++++++ box_sdk_gen/managers/shared_links_folders.py | 546 ++++++++++ .../managers/shared_links_web_links.py | 508 +++++++++ .../shield_information_barrier_reports.py | 183 ++++ ...eld_information_barrier_segment_members.py | 282 +++++ ...nformation_barrier_segment_restrictions.py | 316 ++++++ .../shield_information_barrier_segments.py | 279 +++++ .../managers/shield_information_barriers.py | 219 ++++ box_sdk_gen/managers/shield_lists.py | 300 ++++++ box_sdk_gen/managers/sign_requests.py | 348 +++++++ box_sdk_gen/managers/sign_templates.py | 123 +++ box_sdk_gen/managers/skills.py | 414 ++++++++ box_sdk_gen/managers/storage_policies.py | 140 +++ .../managers/storage_policy_assignments.py | 352 +++++++ box_sdk_gen/managers/task_assignments.py | 303 ++++++ box_sdk_gen/managers/tasks.py | 338 ++++++ .../terms_of_service_user_statuses.py | 235 +++++ box_sdk_gen/managers/terms_of_services.py | 235 +++++ box_sdk_gen/managers/transfer.py | 193 ++++ box_sdk_gen/managers/trashed_files.py | 252 +++++ box_sdk_gen/managers/trashed_folders.py | 279 +++++ box_sdk_gen/managers/trashed_items.py | 171 ++++ box_sdk_gen/managers/trashed_web_links.py | 213 ++++ box_sdk_gen/managers/uploads.py | 546 ++++++++++ box_sdk_gen/managers/user_collaborations.py | 444 ++++++++ box_sdk_gen/managers/users.py | 701 +++++++++++++ box_sdk_gen/managers/web_links.py | 326 ++++++ box_sdk_gen/managers/webhooks.py | 497 +++++++++ box_sdk_gen/managers/workflows.py | 261 +++++ box_sdk_gen/managers/zip_downloads.py | 318 ++++++ box_sdk_gen/networking/__init__.py | 19 + box_sdk_gen/networking/auth.py | 47 + box_sdk_gen/networking/base_urls.py | 29 + box_sdk_gen/networking/box_network_client.py | 363 +++++++ box_sdk_gen/networking/fetch_options.py | 113 ++ box_sdk_gen/networking/fetch_response.py | 36 + box_sdk_gen/networking/network.py | 154 +++ box_sdk_gen/networking/network_client.py | 14 + box_sdk_gen/networking/proxy_config.py | 14 + box_sdk_gen/networking/retries.py | 96 ++ box_sdk_gen/networking/version.py | 1 + box_sdk_gen/parameters/__init__.py | 1 + box_sdk_gen/parameters/v2025_r0/__init__.py | 1 + .../v2025_r0/box_version_header_v2025_r0.py | 7 + box_sdk_gen/schemas/__init__.py | 551 ++++++++++ box_sdk_gen/schemas/access_token.py | 60 ++ box_sdk_gen/schemas/ai_agent.py | 13 + .../schemas/ai_agent_allowed_entity.py | 9 + box_sdk_gen/schemas/ai_agent_ask.py | 48 + .../schemas/ai_agent_basic_gen_tool.py | 69 ++ .../schemas/ai_agent_basic_text_tool.py | 48 + .../schemas/ai_agent_basic_text_tool_base.py | 36 + .../ai_agent_basic_text_tool_text_gen.py | 50 + box_sdk_gen/schemas/ai_agent_extract.py | 38 + .../schemas/ai_agent_extract_structured.py | 38 + box_sdk_gen/schemas/ai_agent_info.py | 49 + .../schemas/ai_agent_long_text_tool.py | 90 ++ .../ai_agent_long_text_tool_text_gen.py | 94 ++ box_sdk_gen/schemas/ai_agent_reference.py | 32 + .../schemas/ai_agent_spreadsheet_tool.py | 36 + box_sdk_gen/schemas/ai_agent_text_gen.py | 32 + box_sdk_gen/schemas/ai_ask.py | 64 ++ box_sdk_gen/schemas/ai_ask_agent.py | 9 + box_sdk_gen/schemas/ai_citation.py | 40 + box_sdk_gen/schemas/ai_dialogue_history.py | 30 + box_sdk_gen/schemas/ai_extract.py | 36 + box_sdk_gen/schemas/ai_extract_agent.py | 9 + box_sdk_gen/schemas/ai_extract_response.py | 8 + box_sdk_gen/schemas/ai_extract_structured.py | 134 +++ .../schemas/ai_extract_structured_agent.py | 9 + .../schemas/ai_extract_structured_response.py | 34 + box_sdk_gen/schemas/ai_item_ask.py | 37 + box_sdk_gen/schemas/ai_item_base.py | 36 + box_sdk_gen/schemas/ai_llm_endpoint_params.py | 18 + .../schemas/ai_llm_endpoint_params_aws.py | 41 + .../schemas/ai_llm_endpoint_params_google.py | 43 + .../schemas/ai_llm_endpoint_params_ibm.py | 47 + .../schemas/ai_llm_endpoint_params_open_ai.py | 54 + .../schemas/ai_multiple_agent_response.py | 38 + box_sdk_gen/schemas/ai_response.py | 34 + box_sdk_gen/schemas/ai_response_full.py | 44 + .../schemas/ai_single_agent_response.py | 78 ++ .../schemas/ai_single_agent_response_full.py | 90 ++ box_sdk_gen/schemas/ai_studio_agent_ask.py | 70 ++ .../schemas/ai_studio_agent_ask_response.py | 72 ++ .../schemas/ai_studio_agent_basic_gen_tool.py | 75 ++ ...ai_studio_agent_basic_gen_tool_response.py | 83 ++ .../ai_studio_agent_basic_text_tool.py | 54 + ...i_studio_agent_basic_text_tool_response.py | 64 ++ .../schemas/ai_studio_agent_extract.py | 52 + .../ai_studio_agent_extract_response.py | 54 + .../schemas/ai_studio_agent_long_text_tool.py | 62 ++ ...ai_studio_agent_long_text_tool_response.py | 70 ++ .../ai_studio_agent_spreadsheet_tool.py | 38 + ..._studio_agent_spreadsheet_tool_response.py | 48 + .../schemas/ai_studio_agent_text_gen.py | 50 + .../ai_studio_agent_text_gen_response.py | 52 + box_sdk_gen/schemas/ai_text_gen.py | 75 ++ box_sdk_gen/schemas/ai_text_gen_agent.py | 9 + box_sdk_gen/schemas/app_item.py | 34 + .../schemas/app_item_associated_item.py | 11 + box_sdk_gen/schemas/app_item_association.py | 44 + box_sdk_gen/schemas/app_item_associations.py | 36 + box_sdk_gen/schemas/app_item_event_source.py | 44 + box_sdk_gen/schemas/classification.py | 88 ++ .../schemas/classification_template.py | 246 +++++ box_sdk_gen/schemas/client_error.py | 77 ++ box_sdk_gen/schemas/collaboration.py | 222 ++++ .../schemas/collaboration_access_grantee.py | 9 + .../collaboration_allowlist_entries.py | 40 + .../schemas/collaboration_allowlist_entry.py | 83 ++ .../collaboration_allowlist_exempt_target.py | 77 ++ .../collaboration_allowlist_exempt_targets.py | 42 + box_sdk_gen/schemas/collaboration_item.py | 11 + box_sdk_gen/schemas/collaborations.py | 38 + .../collaborations_offset_paginated.py | 47 + box_sdk_gen/schemas/collaborator_variable.py | 68 ++ box_sdk_gen/schemas/collection.py | 50 + box_sdk_gen/schemas/collections.py | 80 ++ box_sdk_gen/schemas/comment.py | 66 ++ box_sdk_gen/schemas/comment_base.py | 32 + box_sdk_gen/schemas/comment_full.py | 64 ++ box_sdk_gen/schemas/comments.py | 80 ++ .../schemas/completion_rule_variable.py | 48 + box_sdk_gen/schemas/conflict_error.py | 71 ++ box_sdk_gen/schemas/create_ai_agent.py | 65 ++ box_sdk_gen/schemas/device_pinner.py | 40 + box_sdk_gen/schemas/device_pinners.py | 68 ++ box_sdk_gen/schemas/email_alias.py | 40 + box_sdk_gen/schemas/email_aliases.py | 28 + box_sdk_gen/schemas/enterprise_base.py | 32 + box_sdk_gen/schemas/event.py | 237 +++++ box_sdk_gen/schemas/event_source.py | 63 ++ box_sdk_gen/schemas/event_source_resource.py | 17 + box_sdk_gen/schemas/events.py | 35 + box_sdk_gen/schemas/file.py | 278 +++++ box_sdk_gen/schemas/file_base.py | 44 + box_sdk_gen/schemas/file_conflict.py | 57 ++ box_sdk_gen/schemas/file_full.py | 577 +++++++++++ box_sdk_gen/schemas/file_mini.py | 61 ++ box_sdk_gen/schemas/file_request.py | 129 +++ .../schemas/file_request_copy_request.py | 113 ++ .../schemas/file_request_update_request.py | 80 ++ box_sdk_gen/schemas/file_version.py | 69 ++ box_sdk_gen/schemas/file_version_base.py | 30 + box_sdk_gen/schemas/file_version_full.py | 80 ++ .../schemas/file_version_legal_hold.py | 55 + .../schemas/file_version_legal_holds.py | 38 + box_sdk_gen/schemas/file_version_mini.py | 39 + box_sdk_gen/schemas/file_version_retention.py | 56 + .../schemas/file_version_retentions.py | 38 + box_sdk_gen/schemas/file_versions.py | 80 ++ box_sdk_gen/schemas/files.py | 28 + box_sdk_gen/schemas/files_on_hold.py | 38 + box_sdk_gen/schemas/files_under_retention.py | 38 + box_sdk_gen/schemas/folder.py | 313 ++++++ box_sdk_gen/schemas/folder_base.py | 44 + box_sdk_gen/schemas/folder_full.py | 314 ++++++ box_sdk_gen/schemas/folder_lock.py | 61 ++ box_sdk_gen/schemas/folder_locks.py | 34 + box_sdk_gen/schemas/folder_mini.py | 41 + box_sdk_gen/schemas/folder_reference.py | 30 + box_sdk_gen/schemas/generic_source.py | 1 + box_sdk_gen/schemas/group.py | 44 + box_sdk_gen/schemas/group_base.py | 26 + box_sdk_gen/schemas/group_full.py | 128 +++ box_sdk_gen/schemas/group_membership.py | 59 ++ box_sdk_gen/schemas/group_memberships.py | 80 ++ box_sdk_gen/schemas/group_mini.py | 39 + box_sdk_gen/schemas/groups.py | 80 ++ box_sdk_gen/schemas/integration_mapping.py | 88 ++ .../schemas/integration_mapping_base.py | 32 + .../integration_mapping_box_item_slack.py | 30 + .../integration_mapping_partner_item_slack.py | 40 + .../integration_mapping_partner_item_teams.py | 34 + ...pping_partner_item_teams_create_request.py | 38 + ...ntegration_mapping_slack_create_request.py | 32 + .../integration_mapping_slack_options.py | 21 + .../schemas/integration_mapping_teams.py | 67 ++ ...ntegration_mapping_teams_create_request.py | 21 + box_sdk_gen/schemas/integration_mappings.py | 34 + .../schemas/integration_mappings_teams.py | 21 + box_sdk_gen/schemas/invite.py | 85 ++ box_sdk_gen/schemas/item.py | 11 + box_sdk_gen/schemas/items.py | 94 ++ box_sdk_gen/schemas/items_offset_paginated.py | 86 ++ box_sdk_gen/schemas/keyword_skill_card.py | 139 +++ box_sdk_gen/schemas/legal_hold_policies.py | 38 + box_sdk_gen/schemas/legal_hold_policy.py | 122 +++ .../legal_hold_policy_assigned_item.py | 11 + .../schemas/legal_hold_policy_assignment.py | 63 ++ .../legal_hold_policy_assignment_base.py | 32 + .../schemas/legal_hold_policy_assignments.py | 39 + box_sdk_gen/schemas/legal_hold_policy_mini.py | 30 + box_sdk_gen/schemas/metadata.py | 36 + box_sdk_gen/schemas/metadata_base.py | 55 + .../schemas/metadata_cascade_policies.py | 38 + .../schemas/metadata_cascade_policy.py | 126 +++ box_sdk_gen/schemas/metadata_error.py | 29 + .../metadata_field_filter_date_range.py | 28 + .../metadata_field_filter_float_range.py | 26 + box_sdk_gen/schemas/metadata_filter.py | 77 ++ box_sdk_gen/schemas/metadata_filter_value.py | 17 + box_sdk_gen/schemas/metadata_full.py | 76 ++ .../schemas/metadata_instance_value.py | 7 + box_sdk_gen/schemas/metadata_query.py | 129 +++ .../schemas/metadata_query_result_item.py | 9 + box_sdk_gen/schemas/metadata_query_results.py | 43 + box_sdk_gen/schemas/metadata_template.py | 175 ++++ box_sdk_gen/schemas/metadata_templates.py | 38 + box_sdk_gen/schemas/metadatas.py | 28 + box_sdk_gen/schemas/o_auth_2_error.py | 24 + box_sdk_gen/schemas/outcome.py | 36 + box_sdk_gen/schemas/post_o_auth_2_revoke.py | 30 + box_sdk_gen/schemas/post_o_auth_2_token.py | 146 +++ ...ost_o_auth_2_token_refresh_access_token.py | 36 + box_sdk_gen/schemas/realtime_server.py | 43 + box_sdk_gen/schemas/realtime_servers.py | 28 + box_sdk_gen/schemas/recent_item.py | 56 + box_sdk_gen/schemas/recent_item_resource.py | 11 + box_sdk_gen/schemas/recent_items.py | 38 + box_sdk_gen/schemas/resource.py | 9 + box_sdk_gen/schemas/resource_scope.py | 47 + box_sdk_gen/schemas/retention_policies.py | 34 + box_sdk_gen/schemas/retention_policy.py | 173 ++++ .../schemas/retention_policy_assignment.py | 113 ++ .../schemas/retention_policy_assignments.py | 34 + box_sdk_gen/schemas/retention_policy_base.py | 30 + box_sdk_gen/schemas/retention_policy_mini.py | 54 + box_sdk_gen/schemas/role_variable.py | 47 + box_sdk_gen/schemas/search_result_item.py | 11 + .../schemas/search_result_with_shared_link.py | 39 + .../search_result_with_shared_link_item.py | 11 + box_sdk_gen/schemas/search_results.py | 59 ++ .../schemas/search_results_response.py | 11 + .../search_results_with_shared_links.py | 57 ++ .../schemas/session_termination_message.py | 15 + .../schemas/shield_information_barrier.py | 77 ++ .../shield_information_barrier_base.py | 32 + .../shield_information_barrier_reference.py | 20 + .../shield_information_barrier_report.py | 69 ++ .../shield_information_barrier_report_base.py | 32 + ...ield_information_barrier_report_details.py | 26 + .../shield_information_barrier_reports.py | 37 + .../shield_information_barrier_segment.py | 64 ++ ...ield_information_barrier_segment_member.py | 101 ++ ...information_barrier_segment_member_base.py | 35 + ...information_barrier_segment_member_mini.py | 35 + ...eld_information_barrier_segment_members.py | 37 + ...information_barrier_segment_restriction.py | 83 ++ ...mation_barrier_segment_restriction_base.py | 35 + ...mation_barrier_segment_restriction_mini.py | 107 ++ ...nformation_barrier_segment_restrictions.py | 37 + .../shield_information_barrier_segments.py | 37 + .../schemas/shield_information_barriers.py | 34 + box_sdk_gen/schemas/sign_request.py | 187 ++++ box_sdk_gen/schemas/sign_request_base.py | 72 ++ .../schemas/sign_request_create_request.py | 109 ++ .../schemas/sign_request_create_signer.py | 98 ++ .../schemas/sign_request_prefill_tag.py | 34 + box_sdk_gen/schemas/sign_request_signer.py | 159 +++ .../schemas/sign_request_signer_input.py | 81 ++ box_sdk_gen/schemas/sign_requests.py | 34 + box_sdk_gen/schemas/sign_template.py | 251 +++++ box_sdk_gen/schemas/sign_templates.py | 38 + box_sdk_gen/schemas/skill_card.py | 15 + box_sdk_gen/schemas/skill_cards_metadata.py | 93 ++ box_sdk_gen/schemas/status_skill_card.py | 158 +++ box_sdk_gen/schemas/storage_policies.py | 38 + box_sdk_gen/schemas/storage_policy.py | 28 + .../schemas/storage_policy_assignment.py | 53 + .../schemas/storage_policy_assignments.py | 38 + box_sdk_gen/schemas/storage_policy_mini.py | 30 + box_sdk_gen/schemas/task.py | 87 ++ box_sdk_gen/schemas/task_assignment.py | 76 ++ box_sdk_gen/schemas/task_assignments.py | 28 + box_sdk_gen/schemas/tasks.py | 30 + box_sdk_gen/schemas/template_signer.py | 80 ++ box_sdk_gen/schemas/template_signer_input.py | 147 +++ box_sdk_gen/schemas/terms_of_service.py | 92 ++ box_sdk_gen/schemas/terms_of_service_base.py | 30 + .../schemas/terms_of_service_user_status.py | 54 + .../schemas/terms_of_service_user_statuses.py | 28 + box_sdk_gen/schemas/terms_of_services.py | 28 + box_sdk_gen/schemas/timeline_skill_card.py | 182 ++++ box_sdk_gen/schemas/tracking_code.py | 37 + box_sdk_gen/schemas/transcript_skill_card.py | 169 +++ box_sdk_gen/schemas/trash_file.py | 198 ++++ box_sdk_gen/schemas/trash_file_restored.py | 157 +++ box_sdk_gen/schemas/trash_folder.py | 188 ++++ box_sdk_gen/schemas/trash_folder_restored.py | 147 +++ box_sdk_gen/schemas/trash_web_link.py | 160 +++ .../schemas/trash_web_link_restored.py | 120 +++ box_sdk_gen/schemas/upload_part.py | 42 + box_sdk_gen/schemas/upload_part_mini.py | 30 + box_sdk_gen/schemas/upload_parts.py | 81 ++ box_sdk_gen/schemas/upload_session.py | 95 ++ box_sdk_gen/schemas/upload_url.py | 25 + box_sdk_gen/schemas/uploaded_part.py | 13 + box_sdk_gen/schemas/user.py | 120 +++ box_sdk_gen/schemas/user_avatar.py | 38 + box_sdk_gen/schemas/user_base.py | 26 + box_sdk_gen/schemas/user_collaborations.py | 36 + box_sdk_gen/schemas/user_full.py | 199 ++++ .../schemas/user_integration_mappings.py | 32 + box_sdk_gen/schemas/user_mini.py | 32 + box_sdk_gen/schemas/users.py | 88 ++ box_sdk_gen/schemas/v2025_r0/__init__.py | 125 +++ .../schemas/v2025_r0/archive_v2025_r0.py | 43 + .../schemas/v2025_r0/archives_v2025_r0.py | 34 + .../schemas/v2025_r0/client_error_v2025_r0.py | 77 ++ .../v2025_r0/doc_gen_batch_base_v2025_r0.py | 30 + .../doc_gen_batch_create_request_v2025_r0.py | 71 ++ ...c_gen_document_generation_data_v2025_r0.py | 16 + .../v2025_r0/doc_gen_job_base_v2025_r0.py | 30 + .../v2025_r0/doc_gen_job_full_v2025_r0.py | 82 ++ .../schemas/v2025_r0/doc_gen_job_v2025_r0.py | 64 ++ .../v2025_r0/doc_gen_jobs_full_v2025_r0.py | 38 + .../schemas/v2025_r0/doc_gen_jobs_v2025_r0.py | 38 + .../schemas/v2025_r0/doc_gen_tag_v2025_r0.py | 38 + ...oc_gen_tags_processing_message_v2025_r0.py | 13 + .../schemas/v2025_r0/doc_gen_tags_v2025_r0.py | 38 + .../doc_gen_template_base_v2025_r0.py | 13 + ...oc_gen_template_create_request_v2025_r0.py | 11 + .../v2025_r0/doc_gen_template_v2025_r0.py | 25 + .../v2025_r0/doc_gen_templates_v2025_r0.py | 38 + .../v2025_r0/enterprise_reference_v2025_r0.py | 32 + .../external_user_deletion_result_v2025_r0.py | 23 + ...sers_submit_delete_job_request_v2025_r0.py | 17 + ...ers_submit_delete_job_response_v2025_r0.py | 19 + .../v2025_r0/file_reference_v2025_r0.py | 30 + .../v2025_r0/file_version_base_v2025_r0.py | 30 + .../v2025_r0/folder_reference_v2025_r0.py | 30 + .../schemas/v2025_r0/group_base_v2025_r0.py | 30 + .../schemas/v2025_r0/group_mini_v2025_r0.py | 39 + .../v2025_r0/hub_access_grantee_v2025_r0.py | 11 + .../schemas/v2025_r0/hub_base_v2025_r0.py | 36 + ...b_collaboration_create_request_v2025_r0.py | 84 ++ ...b_collaboration_update_request_v2025_r0.py | 16 + .../hub_collaboration_user_v2025_r0.py | 32 + .../v2025_r0/hub_collaboration_v2025_r0.py | 173 ++++ .../v2025_r0/hub_collaborations_v2025_r0.py | 36 + .../v2025_r0/hub_copy_request_v2025_r0.py | 24 + .../v2025_r0/hub_create_request_v2025_r0.py | 18 + .../hub_item_operation_result_v2025_r0.py | 44 + .../v2025_r0/hub_item_operation_v2025_r0.py | 40 + .../v2025_r0/hub_item_reference_v2025_r0.py | 17 + .../schemas/v2025_r0/hub_item_v2025_r0.py | 29 + .../hub_items_manage_request_v2025_r0.py | 23 + .../hub_items_manage_response_v2025_r0.py | 19 + .../schemas/v2025_r0/hub_items_v2025_r0.py | 34 + .../v2025_r0/hub_update_request_v2025_r0.py | 42 + box_sdk_gen/schemas/v2025_r0/hub_v2025_r0.py | 78 ++ box_sdk_gen/schemas/v2025_r0/hubs_v2025_r0.py | 34 + .../shield_list_content_country_v2025_r0.py | 32 + .../shield_list_content_domain_v2025_r0.py | 32 + .../shield_list_content_email_v2025_r0.py | 32 + ...hield_list_content_integration_v2025_r0.py | 44 + .../shield_list_content_ip_v2025_r0.py | 32 + .../shield_list_content_request_v2025_r0.py | 26 + .../v2025_r0/shield_list_content_v2025_r0.py | 31 + .../v2025_r0/shield_list_mini_v2025_r0.py | 50 + .../schemas/v2025_r0/shield_list_v2025_r0.py | 76 ++ .../v2025_r0/shield_lists_create_v2025_r0.py | 48 + .../v2025_r0/shield_lists_update_v2025_r0.py | 48 + .../schemas/v2025_r0/shield_lists_v2025_r0.py | 21 + .../terms_of_service_base_v2025_r0.py | 30 + .../schemas/v2025_r0/user_base_v2025_r0.py | 30 + .../schemas/v2025_r0/user_mini_v2025_r0.py | 32 + .../v2025_r0/user_reference_v2025_r0.py | 30 + .../v2025_r0/weblink_reference_v2025_r0.py | 30 + box_sdk_gen/schemas/watermark.py | 34 + box_sdk_gen/schemas/web_link.py | 248 +++++ box_sdk_gen/schemas/web_link_base.py | 37 + box_sdk_gen/schemas/web_link_mini.py | 38 + box_sdk_gen/schemas/webhook.py | 99 ++ box_sdk_gen/schemas/webhook_mini.py | 62 ++ box_sdk_gen/schemas/webhooks.py | 38 + box_sdk_gen/schemas/workflow.py | 295 ++++++ box_sdk_gen/schemas/workflow_mini.py | 44 + box_sdk_gen/schemas/workflows.py | 38 + box_sdk_gen/schemas/zip_download.py | 103 ++ box_sdk_gen/schemas/zip_download_request.py | 51 + box_sdk_gen/schemas/zip_download_status.py | 50 + box_sdk_gen/serialization/__init__.py | 1 + box_sdk_gen/serialization/json.py | 66 ++ boxsdk/auth/__init__.py | 2 + boxsdk/auth/ccg_auth.py | 8 +- boxsdk/auth/cooperatively_managed_oauth2.py | 8 +- boxsdk/auth/jwt_auth.py | 82 +- boxsdk/auth/oauth2.py | 99 +- boxsdk/auth/redis_managed_jwt_auth.py | 1 + boxsdk/auth/redis_managed_oauth2.py | 13 +- boxsdk/auth/remote_managed_oauth2.py | 8 +- boxsdk/auth/server_auth.py | 36 +- boxsdk/client/client.py | 620 ++++++----- boxsdk/client/developer_token_client.py | 1 + boxsdk/client/logging_client.py | 1 + boxsdk/config.py | 8 +- boxsdk/exception.py | 42 +- boxsdk/network/default_network.py | 48 +- boxsdk/network/network_interface.py | 8 +- boxsdk/object/__init__.py | 2 +- boxsdk/object/base_api_json_object.py | 15 +- boxsdk/object/base_item.py | 44 +- boxsdk/object/base_object.py | 38 +- boxsdk/object/cloneable.py | 8 +- boxsdk/object/collaboration.py | 15 +- boxsdk/object/collaboration_allowlist.py | 32 +- .../object/collaboration_allowlist_entry.py | 4 +- boxsdk/object/collection.py | 9 +- boxsdk/object/comment.py | 10 +- boxsdk/object/enterprise.py | 2 +- boxsdk/object/events.py | 76 +- boxsdk/object/file.py | 266 +++-- boxsdk/object/file_request.py | 22 +- boxsdk/object/file_version.py | 1 + boxsdk/object/file_version_retention.py | 1 + boxsdk/object/folder.py | 218 ++-- boxsdk/object/group.py | 29 +- boxsdk/object/item.py | 74 +- boxsdk/object/legal_hold.py | 2 +- boxsdk/object/legal_hold_policy.py | 29 +- boxsdk/object/legal_hold_policy_assignment.py | 2 +- boxsdk/object/metadata.py | 13 +- boxsdk/object/metadata_cascade_policy.py | 4 +- boxsdk/object/metadata_template.py | 147 +-- boxsdk/object/retention_policy.py | 21 +- boxsdk/object/retention_policy_assignment.py | 13 +- boxsdk/object/search.py | 171 ++-- boxsdk/object/sign_request.py | 5 +- boxsdk/object/sign_template.py | 1 + boxsdk/object/storage_policy.py | 2 +- boxsdk/object/task.py | 1 + boxsdk/object/task_assignment.py | 1 + boxsdk/object/terms_of_service.py | 14 +- boxsdk/object/terms_of_service_user_status.py | 12 +- boxsdk/object/trash.py | 19 +- boxsdk/object/upload_session.py | 59 +- boxsdk/object/user.py | 40 +- boxsdk/object/web_link.py | 32 +- boxsdk/object/webhook.py | 20 +- boxsdk/pagination/box_object_collection.py | 23 +- .../limit_offset_based_dict_collection.py | 1 + .../limit_offset_based_object_collection.py | 20 +- .../marker_based_dict_collection.py | 1 + .../marker_based_object_collection.py | 22 +- boxsdk/pagination/page.py | 1 + boxsdk/session/box_request.py | 1 + boxsdk/session/session.py | 161 ++- boxsdk/util/api_call_decorator.py | 14 +- boxsdk/util/chunked_uploader.py | 54 +- boxsdk/util/deprecation_decorator.py | 14 +- boxsdk/util/enum.py | 4 +- boxsdk/util/log.py | 8 +- boxsdk/util/multipart_stream.py | 1 + boxsdk/util/shared_link.py | 4 +- boxsdk/util/translator.py | 25 +- demo/auth.py | 5 +- demo/example.py | 33 +- demo/music_player.py | 4 +- docs/source/conf.py | 160 +-- docs/usage/README.md | 2 +- docs/usage/ai.md | 19 +- docs/usage/authentication.md | 56 +- docs/usage/classifications.md | 72 +- docs/usage/collaboration.md | 56 +- docs/usage/collaboration_allowlist.md | 58 +- docs/usage/collections.md | 27 +- docs/usage/comments.md | 40 +- docs/usage/configuration.md | 18 +- docs/usage/device_pin.md | 20 +- docs/usage/events.md | 36 +- docs/usage/file_request.md | 26 +- docs/usage/files.md | 284 +++-- docs/usage/folders.md | 145 ++- docs/usage/group.md | 78 +- docs/usage/legal_hold.md | 75 +- docs/usage/metadata.md | 42 +- docs/usage/metadata_cascade_policies.md | 42 +- docs/usage/retention_policy.md | 95 +- docs/usage/search.md | 31 +- docs/usage/sign_requests.md | 27 +- docs/usage/sign_templates.md | 11 +- docs/usage/storage_policy.md | 57 +- docs/usage/task.md | 83 +- docs/usage/terms_of_service.md | 79 +- docs/usage/trash.md | 39 +- docs/usage/user.md | 85 +- docs/usage/watermarking.md | 37 +- docs/usage/web_link.md | 72 +- docs/usage/webhook.md | 52 +- docs/usage/zip.md | 9 +- setup.py | 18 +- test/conftest.py | 56 +- test/functional/conftest.py | 61 +- .../mock_box/behavior/event_behavior.py | 112 +- .../mock_box/behavior/file_behavior.py | 56 +- .../mock_box/behavior/folder_behavior.py | 89 +- .../mock_box/behavior/oauth2_behavior.py | 93 +- test/functional/mock_box/box.py | 45 +- test/functional/mock_box/db_model/__init__.py | 1 - .../mock_box/db_model/application_model.py | 18 +- .../mock_box/db_model/collaboration_model.py | 17 +- .../mock_box/db_model/event_model.py | 45 +- .../mock_box/db_model/file_model.py | 21 +- .../mock_box/db_model/folder_model.py | 36 +- .../mock_box/db_model/group_model.py | 18 +- .../mock_box/db_model/lock_model.py | 13 +- .../mock_box/db_model/share_model.py | 19 +- .../mock_box/db_model/token_model.py | 13 +- .../mock_box/db_model/user_model.py | 17 +- test/functional/mock_box/util/chaos_utils.py | 18 +- test/functional/mock_box/util/http_utils.py | 10 +- test/functional/mock_box/util/json_utils.py | 18 +- test/functional/test_delete.py | 6 +- test/functional/test_events.py | 95 +- .../test_file_upload_update_download.py | 81 +- test/functional/test_object_clone.py | 16 +- test/functional/test_recovery.py | 4 +- test/functional/test_token_refresh.py | 4 +- test/integration/conftest.py | 4 +- test/integration/mock_network.py | 4 +- test/integration/test_as_user.py | 54 +- test/integration/test_retry_and_refresh.py | 16 +- test/integration/test_with_shared_link.py | 72 +- test/integration_new/__init__.py | 9 +- .../context_managers/box_metadata_template.py | 10 +- .../context_managers/box_retention_policy.py | 34 +- .../box_retention_policy_assigment.py | 6 +- .../context_managers/box_sign_request.py | 4 +- .../context_managers/box_test_file.py | 8 +- .../context_managers/box_test_web_link.py | 8 +- test/integration_new/object/ai_itest.py | 46 +- .../object/collaboration_itest.py | 15 +- test/integration_new/object/file_itest.py | 110 +- test/integration_new/object/folder_itest.py | 98 +- .../object/legal_hold_policy_itest.py | 13 +- .../object/metadata_template_itest.py | 2 +- .../retention_policy_assignement_itest.py | 37 +- .../object/retention_policy_itest.py | 17 +- .../object/sign_request_itest.py | 36 +- test/integration_new/object/trash_itest.py | 8 +- test/integration_new/object/user_itest.py | 4 +- test/integration_new/util.py | 12 +- test/unit/auth/test_ccg_auth.py | 260 +++-- .../auth/test_cooperatively_managed_oauth2.py | 4 +- test/unit/auth/test_jwt_auth.py | 340 ++++-- test/unit/auth/test_oauth2.py | 201 ++-- test/unit/auth/test_redis_managed_oauth2.py | 24 +- test/unit/client/test_client.py | 818 +++++++++------ test/unit/conftest.py | 25 +- test/unit/network/conftest.py | 15 +- test/unit/network/test_network.py | 107 +- test/unit/object/conftest.py | 41 +- test/unit/object/test_base_api_json_object.py | 14 +- test/unit/object/test_base_item.py | 105 +- test/unit/object/test_base_object.py | 84 +- test/unit/object/test_chunked_upload.py | 71 +- test/unit/object/test_collaboration.py | 60 +- .../object/test_collaboration_allowlist.py | 46 +- .../test_collaboration_allowlist_entry.py | 10 +- ...t_collaboration_allowlist_exempt_target.py | 18 +- test/unit/object/test_collection.py | 18 +- test/unit/object/test_comment.py | 29 +- test/unit/object/test_device_pin.py | 16 +- test/unit/object/test_enterprise.py | 18 +- test/unit/object/test_event.py | 6 +- test/unit/object/test_events.py | 180 ++-- test/unit/object/test_file.py | 523 ++++++---- test/unit/object/test_file_request.py | 33 +- .../object/test_file_version_retention.py | 4 +- test/unit/object/test_folder.py | 335 +++--- test/unit/object/test_group.py | 152 +-- test/unit/object/test_item.py | 342 +++++-- test/unit/object/test_legal_hold.py | 8 +- .../unit/object/test_legal_hold_assignment.py | 19 +- test/unit/object/test_legal_hold_policy.py | 76 +- test/unit/object/test_metadata.py | 99 +- .../object/test_metadata_cascade_policy.py | 25 +- test/unit/object/test_metadata_template.py | 29 +- test/unit/object/test_retention_policy.py | 32 +- .../test_retention_policy_assignment.py | 35 +- test/unit/object/test_search.py | 373 ++++--- test/unit/object/test_sign_request.py | 48 +- test/unit/object/test_sign_templates.py | 43 +- test/unit/object/test_storage_policy.py | 41 +- .../object/test_storage_policy_assignment.py | 28 +- test/unit/object/test_task.py | 37 +- test/unit/object/test_task_assignment.py | 34 +- test/unit/object/test_terms_of_service.py | 163 ++- .../test_terms_of_service_user_status.py | 36 +- test/unit/object/test_trash.py | 99 +- test/unit/object/test_upload_session.py | 169 ++- test/unit/object/test_user.py | 109 +- test/unit/object/test_web_link.py | 42 +- test/unit/object/test_webhook.py | 26 +- .../box_object_collection_test_base.py | 59 +- ...st_limit_offset_based_object_collection.py | 52 +- .../test_marker_based_object_collection.py | 30 +- test/unit/pagination/test_page.py | 29 +- test/unit/session/test_session.py | 401 +++++--- test/unit/test_exception.py | 14 +- test/unit/util/test_api_call_decorator.py | 40 +- test/unit/util/test_datetime_formatter.py | 2 +- test/unit/util/test_enum.py | 40 +- test/unit/util/test_log.py | 20 +- test/unit/util/test_multipart_stream.py | 12 +- test/unit/util/test_shared_link.py | 7 +- test/unit/util/test_text_enum.py | 4 +- test/unit/util/test_translator.py | 79 +- test/util/streamable_mock_open.py | 2 +- 680 files changed, 60415 insertions(+), 4867 deletions(-) create mode 100644 .codegen.json create mode 100644 box_sdk_gen/__init__.py create mode 100644 box_sdk_gen/box/__init__.py create mode 100644 box_sdk_gen/box/ccg_auth.py create mode 100644 box_sdk_gen/box/developer_token_auth.py create mode 100644 box_sdk_gen/box/errors.py create mode 100644 box_sdk_gen/box/event_stream.py create mode 100644 box_sdk_gen/box/jwt_auth.py create mode 100644 box_sdk_gen/box/oauth.py create mode 100644 box_sdk_gen/box/token_storage.py create mode 100644 box_sdk_gen/client.py create mode 100644 box_sdk_gen/internal/__init__.py create mode 100644 box_sdk_gen/internal/base_object.py create mode 100644 box_sdk_gen/internal/errors.py create mode 100644 box_sdk_gen/internal/logging.py create mode 100644 box_sdk_gen/internal/null_value.py create mode 100644 box_sdk_gen/internal/utils.py create mode 100644 box_sdk_gen/managers/__init__.py create mode 100644 box_sdk_gen/managers/ai.py create mode 100644 box_sdk_gen/managers/ai_studio.py create mode 100644 box_sdk_gen/managers/app_item_associations.py create mode 100644 box_sdk_gen/managers/archives.py create mode 100644 box_sdk_gen/managers/authorization.py create mode 100644 box_sdk_gen/managers/avatars.py create mode 100644 box_sdk_gen/managers/chunked_uploads.py create mode 100644 box_sdk_gen/managers/classifications.py create mode 100644 box_sdk_gen/managers/collaboration_allowlist_entries.py create mode 100644 box_sdk_gen/managers/collaboration_allowlist_exempt_targets.py create mode 100644 box_sdk_gen/managers/collections.py create mode 100644 box_sdk_gen/managers/comments.py create mode 100644 box_sdk_gen/managers/device_pinners.py create mode 100644 box_sdk_gen/managers/docgen.py create mode 100644 box_sdk_gen/managers/docgen_template.py create mode 100644 box_sdk_gen/managers/downloads.py create mode 100644 box_sdk_gen/managers/email_aliases.py create mode 100644 box_sdk_gen/managers/events.py create mode 100644 box_sdk_gen/managers/external_users.py create mode 100644 box_sdk_gen/managers/file_classifications.py create mode 100644 box_sdk_gen/managers/file_metadata.py create mode 100644 box_sdk_gen/managers/file_requests.py create mode 100644 box_sdk_gen/managers/file_version_legal_holds.py create mode 100644 box_sdk_gen/managers/file_version_retentions.py create mode 100644 box_sdk_gen/managers/file_versions.py create mode 100644 box_sdk_gen/managers/file_watermarks.py create mode 100644 box_sdk_gen/managers/files.py create mode 100644 box_sdk_gen/managers/folder_classifications.py create mode 100644 box_sdk_gen/managers/folder_locks.py create mode 100644 box_sdk_gen/managers/folder_metadata.py create mode 100644 box_sdk_gen/managers/folder_watermarks.py create mode 100644 box_sdk_gen/managers/folders.py create mode 100644 box_sdk_gen/managers/groups.py create mode 100644 box_sdk_gen/managers/hub_collaborations.py create mode 100644 box_sdk_gen/managers/hub_items.py create mode 100644 box_sdk_gen/managers/hubs.py create mode 100644 box_sdk_gen/managers/integration_mappings.py create mode 100644 box_sdk_gen/managers/invites.py create mode 100644 box_sdk_gen/managers/legal_hold_policies.py create mode 100644 box_sdk_gen/managers/legal_hold_policy_assignments.py create mode 100644 box_sdk_gen/managers/list_collaborations.py create mode 100644 box_sdk_gen/managers/memberships.py create mode 100644 box_sdk_gen/managers/metadata_cascade_policies.py create mode 100644 box_sdk_gen/managers/metadata_templates.py create mode 100644 box_sdk_gen/managers/recent_items.py create mode 100644 box_sdk_gen/managers/retention_policies.py create mode 100644 box_sdk_gen/managers/retention_policy_assignments.py create mode 100644 box_sdk_gen/managers/search.py create mode 100644 box_sdk_gen/managers/session_termination.py create mode 100644 box_sdk_gen/managers/shared_links_app_items.py create mode 100644 box_sdk_gen/managers/shared_links_files.py create mode 100644 box_sdk_gen/managers/shared_links_folders.py create mode 100644 box_sdk_gen/managers/shared_links_web_links.py create mode 100644 box_sdk_gen/managers/shield_information_barrier_reports.py create mode 100644 box_sdk_gen/managers/shield_information_barrier_segment_members.py create mode 100644 box_sdk_gen/managers/shield_information_barrier_segment_restrictions.py create mode 100644 box_sdk_gen/managers/shield_information_barrier_segments.py create mode 100644 box_sdk_gen/managers/shield_information_barriers.py create mode 100644 box_sdk_gen/managers/shield_lists.py create mode 100644 box_sdk_gen/managers/sign_requests.py create mode 100644 box_sdk_gen/managers/sign_templates.py create mode 100644 box_sdk_gen/managers/skills.py create mode 100644 box_sdk_gen/managers/storage_policies.py create mode 100644 box_sdk_gen/managers/storage_policy_assignments.py create mode 100644 box_sdk_gen/managers/task_assignments.py create mode 100644 box_sdk_gen/managers/tasks.py create mode 100644 box_sdk_gen/managers/terms_of_service_user_statuses.py create mode 100644 box_sdk_gen/managers/terms_of_services.py create mode 100644 box_sdk_gen/managers/transfer.py create mode 100644 box_sdk_gen/managers/trashed_files.py create mode 100644 box_sdk_gen/managers/trashed_folders.py create mode 100644 box_sdk_gen/managers/trashed_items.py create mode 100644 box_sdk_gen/managers/trashed_web_links.py create mode 100644 box_sdk_gen/managers/uploads.py create mode 100644 box_sdk_gen/managers/user_collaborations.py create mode 100644 box_sdk_gen/managers/users.py create mode 100644 box_sdk_gen/managers/web_links.py create mode 100644 box_sdk_gen/managers/webhooks.py create mode 100644 box_sdk_gen/managers/workflows.py create mode 100644 box_sdk_gen/managers/zip_downloads.py create mode 100644 box_sdk_gen/networking/__init__.py create mode 100644 box_sdk_gen/networking/auth.py create mode 100644 box_sdk_gen/networking/base_urls.py create mode 100644 box_sdk_gen/networking/box_network_client.py create mode 100644 box_sdk_gen/networking/fetch_options.py create mode 100644 box_sdk_gen/networking/fetch_response.py create mode 100644 box_sdk_gen/networking/network.py create mode 100644 box_sdk_gen/networking/network_client.py create mode 100644 box_sdk_gen/networking/proxy_config.py create mode 100644 box_sdk_gen/networking/retries.py create mode 100644 box_sdk_gen/networking/version.py create mode 100644 box_sdk_gen/parameters/__init__.py create mode 100644 box_sdk_gen/parameters/v2025_r0/__init__.py create mode 100644 box_sdk_gen/parameters/v2025_r0/box_version_header_v2025_r0.py create mode 100644 box_sdk_gen/schemas/__init__.py create mode 100644 box_sdk_gen/schemas/access_token.py create mode 100644 box_sdk_gen/schemas/ai_agent.py create mode 100644 box_sdk_gen/schemas/ai_agent_allowed_entity.py create mode 100644 box_sdk_gen/schemas/ai_agent_ask.py create mode 100644 box_sdk_gen/schemas/ai_agent_basic_gen_tool.py create mode 100644 box_sdk_gen/schemas/ai_agent_basic_text_tool.py create mode 100644 box_sdk_gen/schemas/ai_agent_basic_text_tool_base.py create mode 100644 box_sdk_gen/schemas/ai_agent_basic_text_tool_text_gen.py create mode 100644 box_sdk_gen/schemas/ai_agent_extract.py create mode 100644 box_sdk_gen/schemas/ai_agent_extract_structured.py create mode 100644 box_sdk_gen/schemas/ai_agent_info.py create mode 100644 box_sdk_gen/schemas/ai_agent_long_text_tool.py create mode 100644 box_sdk_gen/schemas/ai_agent_long_text_tool_text_gen.py create mode 100644 box_sdk_gen/schemas/ai_agent_reference.py create mode 100644 box_sdk_gen/schemas/ai_agent_spreadsheet_tool.py create mode 100644 box_sdk_gen/schemas/ai_agent_text_gen.py create mode 100644 box_sdk_gen/schemas/ai_ask.py create mode 100644 box_sdk_gen/schemas/ai_ask_agent.py create mode 100644 box_sdk_gen/schemas/ai_citation.py create mode 100644 box_sdk_gen/schemas/ai_dialogue_history.py create mode 100644 box_sdk_gen/schemas/ai_extract.py create mode 100644 box_sdk_gen/schemas/ai_extract_agent.py create mode 100644 box_sdk_gen/schemas/ai_extract_response.py create mode 100644 box_sdk_gen/schemas/ai_extract_structured.py create mode 100644 box_sdk_gen/schemas/ai_extract_structured_agent.py create mode 100644 box_sdk_gen/schemas/ai_extract_structured_response.py create mode 100644 box_sdk_gen/schemas/ai_item_ask.py create mode 100644 box_sdk_gen/schemas/ai_item_base.py create mode 100644 box_sdk_gen/schemas/ai_llm_endpoint_params.py create mode 100644 box_sdk_gen/schemas/ai_llm_endpoint_params_aws.py create mode 100644 box_sdk_gen/schemas/ai_llm_endpoint_params_google.py create mode 100644 box_sdk_gen/schemas/ai_llm_endpoint_params_ibm.py create mode 100644 box_sdk_gen/schemas/ai_llm_endpoint_params_open_ai.py create mode 100644 box_sdk_gen/schemas/ai_multiple_agent_response.py create mode 100644 box_sdk_gen/schemas/ai_response.py create mode 100644 box_sdk_gen/schemas/ai_response_full.py create mode 100644 box_sdk_gen/schemas/ai_single_agent_response.py create mode 100644 box_sdk_gen/schemas/ai_single_agent_response_full.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_ask.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_ask_response.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool_response.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_basic_text_tool.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_basic_text_tool_response.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_extract.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_extract_response.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_long_text_tool.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_long_text_tool_response.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool_response.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_text_gen.py create mode 100644 box_sdk_gen/schemas/ai_studio_agent_text_gen_response.py create mode 100644 box_sdk_gen/schemas/ai_text_gen.py create mode 100644 box_sdk_gen/schemas/ai_text_gen_agent.py create mode 100644 box_sdk_gen/schemas/app_item.py create mode 100644 box_sdk_gen/schemas/app_item_associated_item.py create mode 100644 box_sdk_gen/schemas/app_item_association.py create mode 100644 box_sdk_gen/schemas/app_item_associations.py create mode 100644 box_sdk_gen/schemas/app_item_event_source.py create mode 100644 box_sdk_gen/schemas/classification.py create mode 100644 box_sdk_gen/schemas/classification_template.py create mode 100644 box_sdk_gen/schemas/client_error.py create mode 100644 box_sdk_gen/schemas/collaboration.py create mode 100644 box_sdk_gen/schemas/collaboration_access_grantee.py create mode 100644 box_sdk_gen/schemas/collaboration_allowlist_entries.py create mode 100644 box_sdk_gen/schemas/collaboration_allowlist_entry.py create mode 100644 box_sdk_gen/schemas/collaboration_allowlist_exempt_target.py create mode 100644 box_sdk_gen/schemas/collaboration_allowlist_exempt_targets.py create mode 100644 box_sdk_gen/schemas/collaboration_item.py create mode 100644 box_sdk_gen/schemas/collaborations.py create mode 100644 box_sdk_gen/schemas/collaborations_offset_paginated.py create mode 100644 box_sdk_gen/schemas/collaborator_variable.py create mode 100644 box_sdk_gen/schemas/collection.py create mode 100644 box_sdk_gen/schemas/collections.py create mode 100644 box_sdk_gen/schemas/comment.py create mode 100644 box_sdk_gen/schemas/comment_base.py create mode 100644 box_sdk_gen/schemas/comment_full.py create mode 100644 box_sdk_gen/schemas/comments.py create mode 100644 box_sdk_gen/schemas/completion_rule_variable.py create mode 100644 box_sdk_gen/schemas/conflict_error.py create mode 100644 box_sdk_gen/schemas/create_ai_agent.py create mode 100644 box_sdk_gen/schemas/device_pinner.py create mode 100644 box_sdk_gen/schemas/device_pinners.py create mode 100644 box_sdk_gen/schemas/email_alias.py create mode 100644 box_sdk_gen/schemas/email_aliases.py create mode 100644 box_sdk_gen/schemas/enterprise_base.py create mode 100644 box_sdk_gen/schemas/event.py create mode 100644 box_sdk_gen/schemas/event_source.py create mode 100644 box_sdk_gen/schemas/event_source_resource.py create mode 100644 box_sdk_gen/schemas/events.py create mode 100644 box_sdk_gen/schemas/file.py create mode 100644 box_sdk_gen/schemas/file_base.py create mode 100644 box_sdk_gen/schemas/file_conflict.py create mode 100644 box_sdk_gen/schemas/file_full.py create mode 100644 box_sdk_gen/schemas/file_mini.py create mode 100644 box_sdk_gen/schemas/file_request.py create mode 100644 box_sdk_gen/schemas/file_request_copy_request.py create mode 100644 box_sdk_gen/schemas/file_request_update_request.py create mode 100644 box_sdk_gen/schemas/file_version.py create mode 100644 box_sdk_gen/schemas/file_version_base.py create mode 100644 box_sdk_gen/schemas/file_version_full.py create mode 100644 box_sdk_gen/schemas/file_version_legal_hold.py create mode 100644 box_sdk_gen/schemas/file_version_legal_holds.py create mode 100644 box_sdk_gen/schemas/file_version_mini.py create mode 100644 box_sdk_gen/schemas/file_version_retention.py create mode 100644 box_sdk_gen/schemas/file_version_retentions.py create mode 100644 box_sdk_gen/schemas/file_versions.py create mode 100644 box_sdk_gen/schemas/files.py create mode 100644 box_sdk_gen/schemas/files_on_hold.py create mode 100644 box_sdk_gen/schemas/files_under_retention.py create mode 100644 box_sdk_gen/schemas/folder.py create mode 100644 box_sdk_gen/schemas/folder_base.py create mode 100644 box_sdk_gen/schemas/folder_full.py create mode 100644 box_sdk_gen/schemas/folder_lock.py create mode 100644 box_sdk_gen/schemas/folder_locks.py create mode 100644 box_sdk_gen/schemas/folder_mini.py create mode 100644 box_sdk_gen/schemas/folder_reference.py create mode 100644 box_sdk_gen/schemas/generic_source.py create mode 100644 box_sdk_gen/schemas/group.py create mode 100644 box_sdk_gen/schemas/group_base.py create mode 100644 box_sdk_gen/schemas/group_full.py create mode 100644 box_sdk_gen/schemas/group_membership.py create mode 100644 box_sdk_gen/schemas/group_memberships.py create mode 100644 box_sdk_gen/schemas/group_mini.py create mode 100644 box_sdk_gen/schemas/groups.py create mode 100644 box_sdk_gen/schemas/integration_mapping.py create mode 100644 box_sdk_gen/schemas/integration_mapping_base.py create mode 100644 box_sdk_gen/schemas/integration_mapping_box_item_slack.py create mode 100644 box_sdk_gen/schemas/integration_mapping_partner_item_slack.py create mode 100644 box_sdk_gen/schemas/integration_mapping_partner_item_teams.py create mode 100644 box_sdk_gen/schemas/integration_mapping_partner_item_teams_create_request.py create mode 100644 box_sdk_gen/schemas/integration_mapping_slack_create_request.py create mode 100644 box_sdk_gen/schemas/integration_mapping_slack_options.py create mode 100644 box_sdk_gen/schemas/integration_mapping_teams.py create mode 100644 box_sdk_gen/schemas/integration_mapping_teams_create_request.py create mode 100644 box_sdk_gen/schemas/integration_mappings.py create mode 100644 box_sdk_gen/schemas/integration_mappings_teams.py create mode 100644 box_sdk_gen/schemas/invite.py create mode 100644 box_sdk_gen/schemas/item.py create mode 100644 box_sdk_gen/schemas/items.py create mode 100644 box_sdk_gen/schemas/items_offset_paginated.py create mode 100644 box_sdk_gen/schemas/keyword_skill_card.py create mode 100644 box_sdk_gen/schemas/legal_hold_policies.py create mode 100644 box_sdk_gen/schemas/legal_hold_policy.py create mode 100644 box_sdk_gen/schemas/legal_hold_policy_assigned_item.py create mode 100644 box_sdk_gen/schemas/legal_hold_policy_assignment.py create mode 100644 box_sdk_gen/schemas/legal_hold_policy_assignment_base.py create mode 100644 box_sdk_gen/schemas/legal_hold_policy_assignments.py create mode 100644 box_sdk_gen/schemas/legal_hold_policy_mini.py create mode 100644 box_sdk_gen/schemas/metadata.py create mode 100644 box_sdk_gen/schemas/metadata_base.py create mode 100644 box_sdk_gen/schemas/metadata_cascade_policies.py create mode 100644 box_sdk_gen/schemas/metadata_cascade_policy.py create mode 100644 box_sdk_gen/schemas/metadata_error.py create mode 100644 box_sdk_gen/schemas/metadata_field_filter_date_range.py create mode 100644 box_sdk_gen/schemas/metadata_field_filter_float_range.py create mode 100644 box_sdk_gen/schemas/metadata_filter.py create mode 100644 box_sdk_gen/schemas/metadata_filter_value.py create mode 100644 box_sdk_gen/schemas/metadata_full.py create mode 100644 box_sdk_gen/schemas/metadata_instance_value.py create mode 100644 box_sdk_gen/schemas/metadata_query.py create mode 100644 box_sdk_gen/schemas/metadata_query_result_item.py create mode 100644 box_sdk_gen/schemas/metadata_query_results.py create mode 100644 box_sdk_gen/schemas/metadata_template.py create mode 100644 box_sdk_gen/schemas/metadata_templates.py create mode 100644 box_sdk_gen/schemas/metadatas.py create mode 100644 box_sdk_gen/schemas/o_auth_2_error.py create mode 100644 box_sdk_gen/schemas/outcome.py create mode 100644 box_sdk_gen/schemas/post_o_auth_2_revoke.py create mode 100644 box_sdk_gen/schemas/post_o_auth_2_token.py create mode 100644 box_sdk_gen/schemas/post_o_auth_2_token_refresh_access_token.py create mode 100644 box_sdk_gen/schemas/realtime_server.py create mode 100644 box_sdk_gen/schemas/realtime_servers.py create mode 100644 box_sdk_gen/schemas/recent_item.py create mode 100644 box_sdk_gen/schemas/recent_item_resource.py create mode 100644 box_sdk_gen/schemas/recent_items.py create mode 100644 box_sdk_gen/schemas/resource.py create mode 100644 box_sdk_gen/schemas/resource_scope.py create mode 100644 box_sdk_gen/schemas/retention_policies.py create mode 100644 box_sdk_gen/schemas/retention_policy.py create mode 100644 box_sdk_gen/schemas/retention_policy_assignment.py create mode 100644 box_sdk_gen/schemas/retention_policy_assignments.py create mode 100644 box_sdk_gen/schemas/retention_policy_base.py create mode 100644 box_sdk_gen/schemas/retention_policy_mini.py create mode 100644 box_sdk_gen/schemas/role_variable.py create mode 100644 box_sdk_gen/schemas/search_result_item.py create mode 100644 box_sdk_gen/schemas/search_result_with_shared_link.py create mode 100644 box_sdk_gen/schemas/search_result_with_shared_link_item.py create mode 100644 box_sdk_gen/schemas/search_results.py create mode 100644 box_sdk_gen/schemas/search_results_response.py create mode 100644 box_sdk_gen/schemas/search_results_with_shared_links.py create mode 100644 box_sdk_gen/schemas/session_termination_message.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_base.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_reference.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_report.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_report_base.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_report_details.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_reports.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_member.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_member_base.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_member_mini.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_members.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_restriction.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_restriction_base.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_restriction_mini.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segment_restrictions.py create mode 100644 box_sdk_gen/schemas/shield_information_barrier_segments.py create mode 100644 box_sdk_gen/schemas/shield_information_barriers.py create mode 100644 box_sdk_gen/schemas/sign_request.py create mode 100644 box_sdk_gen/schemas/sign_request_base.py create mode 100644 box_sdk_gen/schemas/sign_request_create_request.py create mode 100644 box_sdk_gen/schemas/sign_request_create_signer.py create mode 100644 box_sdk_gen/schemas/sign_request_prefill_tag.py create mode 100644 box_sdk_gen/schemas/sign_request_signer.py create mode 100644 box_sdk_gen/schemas/sign_request_signer_input.py create mode 100644 box_sdk_gen/schemas/sign_requests.py create mode 100644 box_sdk_gen/schemas/sign_template.py create mode 100644 box_sdk_gen/schemas/sign_templates.py create mode 100644 box_sdk_gen/schemas/skill_card.py create mode 100644 box_sdk_gen/schemas/skill_cards_metadata.py create mode 100644 box_sdk_gen/schemas/status_skill_card.py create mode 100644 box_sdk_gen/schemas/storage_policies.py create mode 100644 box_sdk_gen/schemas/storage_policy.py create mode 100644 box_sdk_gen/schemas/storage_policy_assignment.py create mode 100644 box_sdk_gen/schemas/storage_policy_assignments.py create mode 100644 box_sdk_gen/schemas/storage_policy_mini.py create mode 100644 box_sdk_gen/schemas/task.py create mode 100644 box_sdk_gen/schemas/task_assignment.py create mode 100644 box_sdk_gen/schemas/task_assignments.py create mode 100644 box_sdk_gen/schemas/tasks.py create mode 100644 box_sdk_gen/schemas/template_signer.py create mode 100644 box_sdk_gen/schemas/template_signer_input.py create mode 100644 box_sdk_gen/schemas/terms_of_service.py create mode 100644 box_sdk_gen/schemas/terms_of_service_base.py create mode 100644 box_sdk_gen/schemas/terms_of_service_user_status.py create mode 100644 box_sdk_gen/schemas/terms_of_service_user_statuses.py create mode 100644 box_sdk_gen/schemas/terms_of_services.py create mode 100644 box_sdk_gen/schemas/timeline_skill_card.py create mode 100644 box_sdk_gen/schemas/tracking_code.py create mode 100644 box_sdk_gen/schemas/transcript_skill_card.py create mode 100644 box_sdk_gen/schemas/trash_file.py create mode 100644 box_sdk_gen/schemas/trash_file_restored.py create mode 100644 box_sdk_gen/schemas/trash_folder.py create mode 100644 box_sdk_gen/schemas/trash_folder_restored.py create mode 100644 box_sdk_gen/schemas/trash_web_link.py create mode 100644 box_sdk_gen/schemas/trash_web_link_restored.py create mode 100644 box_sdk_gen/schemas/upload_part.py create mode 100644 box_sdk_gen/schemas/upload_part_mini.py create mode 100644 box_sdk_gen/schemas/upload_parts.py create mode 100644 box_sdk_gen/schemas/upload_session.py create mode 100644 box_sdk_gen/schemas/upload_url.py create mode 100644 box_sdk_gen/schemas/uploaded_part.py create mode 100644 box_sdk_gen/schemas/user.py create mode 100644 box_sdk_gen/schemas/user_avatar.py create mode 100644 box_sdk_gen/schemas/user_base.py create mode 100644 box_sdk_gen/schemas/user_collaborations.py create mode 100644 box_sdk_gen/schemas/user_full.py create mode 100644 box_sdk_gen/schemas/user_integration_mappings.py create mode 100644 box_sdk_gen/schemas/user_mini.py create mode 100644 box_sdk_gen/schemas/users.py create mode 100644 box_sdk_gen/schemas/v2025_r0/__init__.py create mode 100644 box_sdk_gen/schemas/v2025_r0/archive_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/archives_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/client_error_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_batch_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_batch_create_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_document_generation_data_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_job_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_job_full_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_job_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_full_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_tag_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_tags_processing_message_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_tags_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_template_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_template_create_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_template_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/doc_gen_templates_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/enterprise_reference_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/external_user_deletion_result_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_response_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/file_reference_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/file_version_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/folder_reference_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/group_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/group_mini_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_access_grantee_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_collaboration_create_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_collaboration_update_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_collaboration_user_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_collaboration_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_collaborations_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_copy_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_create_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_item_operation_result_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_item_operation_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_item_reference_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_item_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_items_manage_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_items_manage_response_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_items_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_update_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hub_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/hubs_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_content_country_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_content_domain_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_content_email_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_content_integration_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_content_ip_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_content_request_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_content_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_mini_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_list_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_lists_create_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_lists_update_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/shield_lists_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/terms_of_service_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/user_base_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/user_mini_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/user_reference_v2025_r0.py create mode 100644 box_sdk_gen/schemas/v2025_r0/weblink_reference_v2025_r0.py create mode 100644 box_sdk_gen/schemas/watermark.py create mode 100644 box_sdk_gen/schemas/web_link.py create mode 100644 box_sdk_gen/schemas/web_link_base.py create mode 100644 box_sdk_gen/schemas/web_link_mini.py create mode 100644 box_sdk_gen/schemas/webhook.py create mode 100644 box_sdk_gen/schemas/webhook_mini.py create mode 100644 box_sdk_gen/schemas/webhooks.py create mode 100644 box_sdk_gen/schemas/workflow.py create mode 100644 box_sdk_gen/schemas/workflow_mini.py create mode 100644 box_sdk_gen/schemas/workflows.py create mode 100644 box_sdk_gen/schemas/zip_download.py create mode 100644 box_sdk_gen/schemas/zip_download_request.py create mode 100644 box_sdk_gen/schemas/zip_download_status.py create mode 100644 box_sdk_gen/serialization/__init__.py create mode 100644 box_sdk_gen/serialization/json.py diff --git a/.codegen.json b/.codegen.json new file mode 100644 index 000000000..c73b83f6e --- /dev/null +++ b/.codegen.json @@ -0,0 +1 @@ +{ "engineHash": "a356057", "specHash": "a05e5d7", "version": "0.1.0" } diff --git a/box_sdk_gen/__init__.py b/box_sdk_gen/__init__.py new file mode 100644 index 000000000..6b93cb29f --- /dev/null +++ b/box_sdk_gen/__init__.py @@ -0,0 +1,15 @@ +from box_sdk_gen.box import * + +from box_sdk_gen.serialization import * + +from box_sdk_gen.internal import * + +from box_sdk_gen.networking import * + +from box_sdk_gen.schemas import * + +from box_sdk_gen.parameters import * + +from box_sdk_gen.managers import * + +from box_sdk_gen.client import * diff --git a/box_sdk_gen/box/__init__.py b/box_sdk_gen/box/__init__.py new file mode 100644 index 000000000..877f6093d --- /dev/null +++ b/box_sdk_gen/box/__init__.py @@ -0,0 +1,13 @@ +from box_sdk_gen.box.event_stream import * + +from box_sdk_gen.box.errors import * + +from box_sdk_gen.box.token_storage import * + +from box_sdk_gen.box.developer_token_auth import * + +from box_sdk_gen.box.oauth import * + +from box_sdk_gen.box.jwt_auth import * + +from box_sdk_gen.box.ccg_auth import * diff --git a/box_sdk_gen/box/ccg_auth.py b/box_sdk_gen/box/ccg_auth.py new file mode 100644 index 000000000..f4dbd05a8 --- /dev/null +++ b/box_sdk_gen/box/ccg_auth.py @@ -0,0 +1,236 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenGrantTypeField + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenSubjectTokenTypeField + +from box_sdk_gen.schemas.access_token import AccessToken + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenBoxSubjectTypeField + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.box.token_storage import TokenStorage + +from box_sdk_gen.box.token_storage import InMemoryTokenStorage + +from box_sdk_gen.managers.authorization import AuthorizationManager + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2Token + +from box_sdk_gen.schemas.post_o_auth_2_revoke import PostOAuth2Revoke + + +class CCGConfig: + def __init__( + self, + client_id: str, + client_secret: str, + *, + enterprise_id: Optional[str] = None, + user_id: Optional[str] = None, + token_storage: TokenStorage = None + ): + """ + :param client_id: Box API key used for identifying the application the user is authenticating with + :type client_id: str + :param client_secret: Box API secret used for making auth requests. + :type client_secret: str + :param enterprise_id: The ID of the Box Developer Edition enterprise., defaults to None + :type enterprise_id: Optional[str], optional + :param user_id: The user id to authenticate. This value is not required. But if it is provided, then the user will be auto-authenticated at the time of the first API call., defaults to None + :type user_id: Optional[str], optional + :param token_storage: Object responsible for storing token. If no custom implementation provided,the token will be stored in memory., defaults to None + :type token_storage: TokenStorage, optional + """ + if token_storage is None: + token_storage = InMemoryTokenStorage() + self.client_id = client_id + self.client_secret = client_secret + self.enterprise_id = enterprise_id + self.user_id = user_id + self.token_storage = token_storage + + +class BoxCCGAuth(Authentication): + def __init__(self, config: CCGConfig, **kwargs): + """ + :param config: Configuration object of Client Credentials Grant auth. + :type config: CCGConfig + """ + super().__init__(**kwargs) + self.config = config + self.token_storage = self.config.token_storage + self.subject_id = ( + self.config.user_id + if not self.config.user_id == None + else self.config.enterprise_id + ) + self.subject_type = ( + PostOAuth2TokenBoxSubjectTypeField.USER + if not self.config.user_id == None + else PostOAuth2TokenBoxSubjectTypeField.ENTERPRISE + ) + + def refresh_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Get a new access token using CCG auth + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.CLIENT_CREDENTIALS, + client_id=self.config.client_id, + client_secret=self.config.client_secret, + box_subject_type=self.subject_type, + box_subject_id=self.subject_id, + ) + self.token_storage.store(token) + return token + + def retrieve_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Return a current token or get a new one when not available. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + old_token: Optional[AccessToken] = self.token_storage.get() + if old_token == None: + new_token: AccessToken = self.refresh_token(network_session=network_session) + return new_token + return old_token + + def retrieve_authorization_header( + self, *, network_session: Optional[NetworkSession] = None + ) -> str: + token: AccessToken = self.retrieve_token(network_session=network_session) + return ''.join(['Bearer ', token.access_token]) + + def with_user_subject( + self, user_id: str, *, token_storage: TokenStorage = None + ) -> 'BoxCCGAuth': + """ + Create a new BoxCCGAuth instance that uses the provided user ID as the subject ID. + + May be one of this application's created App User. Depending on the configured User Access Level, may also be any other App User or Managed User in the enterprise. + + + + + + + + :param user_id: The id of the user to authenticate + :type user_id: str + :param token_storage: Object responsible for storing token in newly created BoxCCGAuth. If no custom implementation provided, the token will be stored in memory., defaults to None + :type token_storage: TokenStorage, optional + """ + if token_storage is None: + token_storage = InMemoryTokenStorage() + new_config: CCGConfig = CCGConfig( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + enterprise_id=self.config.enterprise_id, + user_id=user_id, + token_storage=token_storage, + ) + return BoxCCGAuth(config=new_config) + + def with_enterprise_subject( + self, enterprise_id: str, *, token_storage: TokenStorage = None + ) -> 'BoxCCGAuth': + """ + Create a new BoxCCGAuth instance that uses the provided enterprise ID as the subject ID. + :param enterprise_id: The id of the enterprise to authenticate + :type enterprise_id: str + :param token_storage: Object responsible for storing token in newly created BoxCCGAuth. If no custom implementation provided, the token will be stored in memory., defaults to None + :type token_storage: TokenStorage, optional + """ + if token_storage is None: + token_storage = InMemoryTokenStorage() + new_config: CCGConfig = CCGConfig( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + enterprise_id=enterprise_id, + user_id=None, + token_storage=token_storage, + ) + return BoxCCGAuth(config=new_config) + + def downscope_token( + self, + scopes: List[str], + *, + resource: Optional[str] = None, + shared_link: Optional[str] = None, + network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Downscope access token to the provided scopes. Returning a new access token with the provided scopes, with the original access token unchanged. + :param scopes: The scope(s) to apply to the resulting token. + :type scopes: List[str] + :param resource: The file or folder to get a downscoped token for. If None and shared_link None, the resulting token will not be scoped down to just a single item. The resource should be a full URL to an item, e.g. https://api.box.com/2.0/files/123456., defaults to None + :type resource: Optional[str], optional + :param shared_link: The shared link to get a downscoped token for. If None and item None, the resulting token will not be scoped down to just a single item., defaults to None + :type shared_link: Optional[str], optional + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.retrieve_token( + network_session=network_session + ) + if token == None: + raise BoxSDKError( + message='No access token is available. Make an API call to retrieve a token before calling this method.' + ) + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + downscoped_token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE, + subject_token=token.access_token, + subject_token_type=PostOAuth2TokenSubjectTokenTypeField.URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN, + resource=resource, + scope=' '.join(scopes), + box_shared_link=shared_link, + ) + return downscoped_token + + def revoke_token(self, *, network_session: Optional[NetworkSession] = None) -> None: + """ + Revoke the current access token and remove it from token storage. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + old_token: Optional[AccessToken] = self.token_storage.get() + if old_token == None: + return None + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + auth_manager.revoke_access_token( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + token=old_token.access_token, + ) + self.token_storage.clear() + return None diff --git a/box_sdk_gen/box/developer_token_auth.py b/box_sdk_gen/box/developer_token_auth.py new file mode 100644 index 000000000..8c803c8a4 --- /dev/null +++ b/box_sdk_gen/box/developer_token_auth.py @@ -0,0 +1,139 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenGrantTypeField + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenSubjectTokenTypeField + +from box_sdk_gen.schemas.access_token import AccessToken + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.box.token_storage import TokenStorage + +from box_sdk_gen.box.token_storage import InMemoryTokenStorage + +from box_sdk_gen.managers.authorization import AuthorizationManager + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2Token + +from box_sdk_gen.schemas.post_o_auth_2_revoke import PostOAuth2Revoke + + +class DeveloperTokenConfig: + def __init__( + self, *, client_id: Optional[str] = None, client_secret: Optional[str] = None + ): + self.client_id = client_id + self.client_secret = client_secret + + +class BoxDeveloperTokenAuth(Authentication): + def __init__(self, token: str, *, config: DeveloperTokenConfig = None, **kwargs): + """ + :param config: Configuration object of DeveloperTokenAuth., defaults to None + :type config: DeveloperTokenConfig, optional + """ + super().__init__(**kwargs) + self.token = token + self.config = config + self.token_storage = InMemoryTokenStorage( + token=AccessToken(access_token=self.token) + ) + + def retrieve_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Retrieves stored developer token + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.token_storage.get() + if token == None: + raise BoxSDKError(message='No access token is available.') + return token + + def refresh_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Developer token cannot be refreshed + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + raise BoxSDKError( + message='Developer token has expired. Please provide a new one.' + ) + + def retrieve_authorization_header( + self, *, network_session: Optional[NetworkSession] = None + ) -> str: + token: AccessToken = self.retrieve_token(network_session=network_session) + return ''.join(['Bearer ', token.access_token]) + + def revoke_token(self, *, network_session: Optional[NetworkSession] = None) -> None: + """ + Revoke an active Access Token, effectively logging a user out that has been previously authenticated. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.token_storage.get() + if token == None: + return None + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + auth_manager.revoke_access_token( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + token=token.access_token, + ) + self.token_storage.clear() + return None + + def downscope_token( + self, + scopes: List[str], + *, + resource: Optional[str] = None, + shared_link: Optional[str] = None, + network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Downscope access token to the provided scopes. Returning a new access token with the provided scopes, with the original access token unchanged. + :param scopes: The scope(s) to apply to the resulting token. + :type scopes: List[str] + :param resource: The file or folder to get a downscoped token for. If None and shared_link None, the resulting token will not be scoped down to just a single item. The resource should be a full URL to an item, e.g. https://api.box.com/2.0/files/123456., defaults to None + :type resource: Optional[str], optional + :param shared_link: The shared link to get a downscoped token for. If None and item None, the resulting token will not be scoped down to just a single item., defaults to None + :type shared_link: Optional[str], optional + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.retrieve_token( + network_session=network_session + ) + if token == None or token.access_token == None: + raise BoxSDKError(message='No access token is available.') + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + downscoped_token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE, + subject_token=token.access_token, + subject_token_type=PostOAuth2TokenSubjectTokenTypeField.URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN, + resource=resource, + scope=' '.join(scopes), + box_shared_link=shared_link, + ) + return downscoped_token diff --git a/box_sdk_gen/box/errors.py b/box_sdk_gen/box/errors.py new file mode 100644 index 000000000..b1baf3d72 --- /dev/null +++ b/box_sdk_gen/box/errors.py @@ -0,0 +1,136 @@ +import pprint +from datetime import datetime +from typing import Any, Dict, Optional + +from ..internal.logging import DataSanitizer +from ..internal.errors import GeneratedCodeError + + +class BoxSDKError(GeneratedCodeError): + def __init__( + self, + message: str, + timestamp: Optional[datetime] = None, + error: Optional[Exception] = None, + **kwargs, + ): + super().__init__(message, **kwargs) + self.name = 'BoxSDKError' + self.message = message + self.timestamp = timestamp if timestamp is not None else datetime.now() + self.error = error + + def __str__(self): + return ''.join( + ( + f'\nTimestamp: {self.timestamp}', + f'\nUnderlying error: {self.error}', + f'\nMessage: {self.message}', + ) + ) + + +class RequestInfo: + def __init__( + self, + method: str, + url: str, + query_params: Dict[str, str], + headers: Dict[str, str], + body: Optional[str] = None, + ): + self.method = method + self.url = url + self.query_params = query_params + self.headers = headers + self.body = body + + def print(self, data_sanitizer: DataSanitizer): + return ''.join( + ( + f'\n\tMethod: {self.method}', + f'\n\tURL: {self.url}', + f'\n\tQuery params: \n{pprint.pformat(self.query_params, indent=8)}', + f'\n\tHeaders: \n{pprint.pformat(data_sanitizer.sanitize_headers(self.headers), indent=8)}', + ''.join( + [ + '\n\tBody: ', + '\n' if self.body else '', + pprint.pformat(self.body, indent=8), + ] + ), + ) + ) + + +class ResponseInfo: + def __init__( + self, + status_code: int, + headers: Dict[str, str], + body: Dict = None, + raw_body: Optional[str] = None, + code: Optional[str] = None, + context_info: Optional[Dict[str, Any]] = None, + request_id: Optional[str] = None, + help_url: Optional[str] = None, + ): + self.status_code = status_code + self.headers = headers + self.body = body + self.raw_body = raw_body + self.code = code + self.context_info = context_info + self.request_id = request_id + self.help_url = help_url + + def print(self, data_sanitizer: DataSanitizer): + return ''.join( + ( + f'\n\tStatus code: {self.status_code}', + f'\n\tHeaders: \n{pprint.pformat(data_sanitizer.sanitize_headers(self.headers), indent=8)}', + f'\n\tCode: {self.code}', + f'\n\tContext Info: \n{pprint.pformat(self.context_info, indent=8)}', + f'\n\tRequest Id: {self.request_id}', + f'\n\tHelp Url: {self.help_url}', + ''.join( + [ + '\n\tBody: ', + '\n' if self.body else '', + pprint.pformat( + data_sanitizer.sanitize_body(self.body), indent=8 + ), + ] + ), + ) + ) + + +class BoxAPIError(BoxSDKError): + def __init__( + self, + request_info: RequestInfo, + response_info: ResponseInfo, + message: str, + timestamp: Optional[datetime] = None, + error: Optional[str] = None, + *, + data_sanitizer: DataSanitizer = None, + **kwargs, + ): + super().__init__(message=message, timestamp=timestamp, error=error, **kwargs) + if data_sanitizer is None: + data_sanitizer = DataSanitizer() + self.name = 'BoxAPIError' + self.request_info = request_info + self.response_info = response_info + self.data_sanitizer = data_sanitizer + + def __str__(self): + return ''.join( + [ + f'\t{super(BoxAPIError, self).__str__()}', + f'\nRequest: {self.request_info.print(self.data_sanitizer)}', + f'\nResponse: {self.response_info.print(self.data_sanitizer)}', + ] + ) diff --git a/box_sdk_gen/box/event_stream.py b/box_sdk_gen/box/event_stream.py new file mode 100644 index 000000000..23b2b3b61 --- /dev/null +++ b/box_sdk_gen/box/event_stream.py @@ -0,0 +1,236 @@ +import threading +from enum import Enum +from typing import Optional, Generator + +from ..box.errors import BoxSDKError +from ..schemas.events import Events +from ..schemas.event import Event +from ..schemas.realtime_server import RealtimeServer +from ..networking.fetch_options import FetchOptions, ResponseFormat +from ..networking.fetch_response import FetchResponse + + +class RealtimeServerEvent(str, Enum): + NEW_CHANGE = 'new_change' + RECONNECT = 'reconnect' + + +class EventStreamAction(str, Enum): + FETCH_EVENTS = 'fetch_events' + RECONNECT = 'reconnect' + RETRY = 'retry' + STOP = 'stop' + + +class EventStream: + """ + EventStream is an iterator that fetches events from the Box API. + It uses long polling to receive real-time updates. + This class is designed to be used as a Python iterator. + + Example usage: + events_stream = client.events.get_event_stream() + for event in events_stream: + print(event) + """ + + def __init__(self, *, events_manager, query_params, headers_input): + """ + Initialize the EventStream. + + :param events_manager: The EventsManager instance which provides relevant methods to fetch events. + :param query_params: The query parameters to use for fetching events. + :param headers_input: The headers to include in the request. + """ + self._events_manager = events_manager + self._query_params = query_params + self._headers_input = headers_input + self._stream_position = query_params.stream_position or 'now' + self._long_polling_info: Optional[RealtimeServer] = None + self._long_polling_retries: int = 0 + self._started: bool = False + self._stopped: bool = False + self._stop_event = threading.Event() + self._deduplication_size = 1000 + self._dedupHash = dict() + + def __iter__(self) -> Generator[Event, None, None]: + """Make EventStream iterable. Yields Event objects.""" + return self._event_generator() + + def _event_generator(self) -> Generator[Event, None, None]: + """Generator that yields Event objects from the stream.""" + if not self._started: + self._started = True + + try: + # Start with fetching events to get initial events and stream position + yield from self._fetch_events() + + # Then start long polling loop + while not self._stopped and not self._stop_event.is_set(): + try: + action = self._get_long_poll_info_and_poll() + + if action == EventStreamAction.FETCH_EVENTS: + # Fetch new events when notified + yield from self._fetch_events() + elif action == EventStreamAction.RECONNECT: + # Continue the loop to get new long polling info + continue + elif action == EventStreamAction.RETRY: + # Wait a bit before retrying + if not self._stop_event.wait(5): + continue + else: + break + elif action == EventStreamAction.STOP: + break + else: + # Continue long polling + continue + + except Exception as e: + if not self._stopped and not self._stop_event.is_set(): + # Wait a bit before retrying + if not self._stop_event.wait(5): + continue + break + + except Exception as e: + if not self._stopped and not self._stop_event.is_set(): + pass + return + + def stop(self): + """Stop the event stream.""" + self._stopped = True + self._stop_event.set() + + def _get_long_poll_info(self): + """Fetch long polling info from the server.""" + if self._stopped or self._stop_event.is_set(): + return + + try: + info = self._events_manager.get_events_with_long_polling() + + server = next( + (e for e in (info.entries or []) if e.type == 'realtime_server'), None + ) + if not server: + raise BoxSDKError(message='No realtime server found in the response.') + + self._long_polling_info = server + self._long_polling_retries = 0 + + except Exception as error: + if not self._stopped and not self._stop_event.is_set(): + raise error + + def _get_long_poll_info_and_poll(self) -> str: + """Get long polling info and perform a long poll, returning the action to take.""" + if self._stopped or self._stop_event.is_set(): + return 'stop' + + # Get long polling info if needed + if not self._long_polling_info or self._long_polling_retries > int( + self._long_polling_info.max_retries or '10' + ): + self._get_long_poll_info() + + return self._do_long_poll() + + def _do_long_poll(self) -> str: + """Perform the long polling request and return action to take.""" + if self._stopped or self._stop_event.is_set(): + return EventStreamAction.STOP + + try: + self._long_polling_retries += 1 + + long_poll_url = self._long_polling_info.url + separator = '&' if '?' in long_poll_url else '?' + long_poll_with_stream_position = ( + f"{long_poll_url}{separator}stream_position={self._stream_position}" + ) + + response: FetchResponse = ( + self._events_manager.network_session.network_client.fetch( + FetchOptions( + url=long_poll_with_stream_position, + method='GET', + headers={ + 'Content-Type': 'application/json', + }, + response_format=ResponseFormat.JSON, + auth=self._events_manager.auth, + network_session=self._events_manager.network_session, + ) + ) + ) + + if self._stopped or self._stop_event.is_set(): + return EventStreamAction.STOP + + if response.data: + message = response.data + + if isinstance(message, dict): + message_text = message.get('message', '') + + if message_text == RealtimeServerEvent.NEW_CHANGE: + return EventStreamAction.FETCH_EVENTS + elif message_text == RealtimeServerEvent.RECONNECT: + return EventStreamAction.RECONNECT + + # Continue long polling + return self._do_long_poll() + + except Exception as error: + if not self._stopped and not self._stop_event.is_set(): + return 'retry' + + return 'stop' + + def _fetch_events(self) -> Generator[Event, None, None]: + """Fetch events from the API and yield Event objects.""" + if self._stopped or self._stop_event.is_set(): + return + + try: + # Prepare query parameters for the get_events call + fetch_params = self._query_params.__dict__ + fetch_params['stream_position'] = self._stream_position + + # Add extra headers if provided + if self._headers_input and self._headers_input.extra_headers: + fetch_params['extra_headers'] = self._headers_input.extra_headers + + events: Events = self._events_manager.get_events(**fetch_params) + + # Update stream position for next request + if events.next_stream_position is not None: + self._stream_position = str(events.next_stream_position) + else: + self._stream_position = 'now' + + # Yield Event objects if any + if events.entries: + for event in events.entries: + event_id = event.event_id + if event_id not in self._dedupHash: + self._dedupHash[event_id] = True + if self._stopped or self._stop_event.is_set(): + return + yield event + + if len(self._dedupHash) > self._deduplication_size: + self._dedupHash.clear() + event_ids = list(events.entries.map(lambda e: e.event_id)) + for event_id in event_ids: + self._dedupHash[event_id] = True + + except Exception as error: + if not self._stopped and not self._stop_event.is_set(): + raise error diff --git a/box_sdk_gen/box/jwt_auth.py b/box_sdk_gen/box/jwt_auth.py new file mode 100644 index 000000000..0040f7da9 --- /dev/null +++ b/box_sdk_gen/box/jwt_auth.py @@ -0,0 +1,478 @@ +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from box_sdk_gen.serialization.json import deserialize + +from typing import List + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenGrantTypeField + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenSubjectTokenTypeField + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.schemas.access_token import AccessToken + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2Token + +from box_sdk_gen.schemas.post_o_auth_2_revoke import PostOAuth2Revoke + +from box_sdk_gen.box.token_storage import TokenStorage + +from box_sdk_gen.box.token_storage import InMemoryTokenStorage + +from box_sdk_gen.serialization.json import json_to_serialized_data + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import get_uuid + +from box_sdk_gen.internal.utils import read_text_from_file + +from box_sdk_gen.internal.utils import is_browser + +from box_sdk_gen.internal.utils import get_epoch_time_in_seconds + +from box_sdk_gen.internal.utils import create_jwt_assertion + +from box_sdk_gen.internal.utils import JwtSignOptions + +from box_sdk_gen.internal.utils import JwtKey + +from box_sdk_gen.internal.utils import JwtAlgorithm + +from box_sdk_gen.internal.utils import PrivateKeyDecryptor + +from box_sdk_gen.internal.utils import DefaultPrivateKeyDecryptor + +from box_sdk_gen.managers.authorization import AuthorizationManager + +from box_sdk_gen.box.errors import BoxSDKError + + +class JwtConfigAppSettingsAppAuth(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'public_key_id': 'publicKeyID', + 'private_key': 'privateKey', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'publicKeyID': 'public_key_id', + 'privateKey': 'private_key', + **BaseObject._json_to_fields_mapping, + } + + def __init__(self, public_key_id: str, private_key: str, passphrase: str, **kwargs): + """ + :param public_key_id: Public key ID + :type public_key_id: str + :param private_key: Private key + :type private_key: str + :param passphrase: Passphrase + :type passphrase: str + """ + super().__init__(**kwargs) + self.public_key_id = public_key_id + self.private_key = private_key + self.passphrase = passphrase + + +class JwtConfigAppSettings(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'client_id': 'clientID', + 'client_secret': 'clientSecret', + 'app_auth': 'appAuth', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'clientID': 'client_id', + 'clientSecret': 'client_secret', + 'appAuth': 'app_auth', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + client_id: str, + client_secret: str, + app_auth: JwtConfigAppSettingsAppAuth, + **kwargs + ): + """ + :param client_id: App client ID + :type client_id: str + :param client_secret: App client secret + :type client_secret: str + :param app_auth: App auth settings + :type app_auth: JwtConfigAppSettingsAppAuth + """ + super().__init__(**kwargs) + self.client_id = client_id + self.client_secret = client_secret + self.app_auth = app_auth + + +class JwtConfigFile(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'enterprise_id': 'enterpriseID', + 'user_id': 'userID', + 'box_app_settings': 'boxAppSettings', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'enterpriseID': 'enterprise_id', + 'userID': 'user_id', + 'boxAppSettings': 'box_app_settings', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + box_app_settings: JwtConfigAppSettings, + *, + enterprise_id: Optional[str] = None, + user_id: Optional[str] = None, + **kwargs + ): + """ + :param box_app_settings: App settings + :type box_app_settings: JwtConfigAppSettings + :param enterprise_id: Enterprise ID, defaults to None + :type enterprise_id: Optional[str], optional + :param user_id: User ID, defaults to None + :type user_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.box_app_settings = box_app_settings + self.enterprise_id = enterprise_id + self.user_id = user_id + + +class JWTConfig: + def __init__( + self, + client_id: str, + client_secret: str, + jwt_key_id: str, + private_key: str, + private_key_passphrase: str, + *, + enterprise_id: Optional[str] = None, + user_id: Optional[str] = None, + algorithm: Optional[JwtAlgorithm] = JwtAlgorithm.RS256, + token_storage: TokenStorage = None, + private_key_decryptor: PrivateKeyDecryptor = None + ): + """ + :param client_id: App client ID + :type client_id: str + :param client_secret: App client secret + :type client_secret: str + :param jwt_key_id: Public key ID + :type jwt_key_id: str + :param private_key: Private key + :type private_key: str + :param private_key_passphrase: Passphrase + :type private_key_passphrase: str + :param enterprise_id: Enterprise ID, defaults to None + :type enterprise_id: Optional[str], optional + :param user_id: User ID, defaults to None + :type user_id: Optional[str], optional + """ + if token_storage is None: + token_storage = InMemoryTokenStorage() + if private_key_decryptor is None: + private_key_decryptor = DefaultPrivateKeyDecryptor() + self.client_id = client_id + self.client_secret = client_secret + self.jwt_key_id = jwt_key_id + self.private_key = private_key + self.private_key_passphrase = private_key_passphrase + self.enterprise_id = enterprise_id + self.user_id = user_id + self.algorithm = algorithm + self.token_storage = token_storage + self.private_key_decryptor = private_key_decryptor + + @staticmethod + def from_config_json_string( + config_json_string: str, + *, + token_storage: Optional[TokenStorage] = None, + private_key_decryptor: Optional[PrivateKeyDecryptor] = None + ) -> 'JWTConfig': + """ + Create an auth instance as defined by a string content of JSON file downloaded from the Box Developer Console. + + See https://developer.box.com/en/guides/authentication/jwt/ for more information. + + :param config_json_string: String content of JSON file containing the configuration. + :type config_json_string: str + :param token_storage: Object responsible for storing token. If no custom implementation provided, the token will be stored in memory, defaults to None + :type token_storage: Optional[TokenStorage], optional + :param private_key_decryptor: Object responsible for decrypting private key for jwt auth. If no custom implementation provided, the DefaultPrivateKeyDecryptor will be used., defaults to None + :type private_key_decryptor: Optional[PrivateKeyDecryptor], optional + """ + config_json: JwtConfigFile = deserialize( + json_to_serialized_data(config_json_string), JwtConfigFile + ) + token_storage_to_use: Optional[TokenStorage] = ( + InMemoryTokenStorage() if token_storage == None else token_storage + ) + private_key_decryptor_to_use: Optional[PrivateKeyDecryptor] = ( + DefaultPrivateKeyDecryptor() + if private_key_decryptor == None + else private_key_decryptor + ) + new_config: 'JWTConfig' = JWTConfig( + client_id=config_json.box_app_settings.client_id, + client_secret=config_json.box_app_settings.client_secret, + enterprise_id=config_json.enterprise_id, + user_id=config_json.user_id, + jwt_key_id=config_json.box_app_settings.app_auth.public_key_id, + private_key=config_json.box_app_settings.app_auth.private_key, + private_key_passphrase=config_json.box_app_settings.app_auth.passphrase, + token_storage=token_storage_to_use, + private_key_decryptor=private_key_decryptor_to_use, + ) + return new_config + + @staticmethod + def from_config_file( + config_file_path: str, + *, + token_storage: Optional[TokenStorage] = None, + private_key_decryptor: Optional[PrivateKeyDecryptor] = None + ) -> 'JWTConfig': + """ + Create an auth instance as defined by a JSON file downloaded from the Box Developer Console. + + See https://developer.box.com/en/guides/authentication/jwt/ for more information. + + :param config_file_path: Path to the JSON file containing the configuration. + :type config_file_path: str + :param token_storage: Object responsible for storing token. If no custom implementation provided, the token will be stored in memory., defaults to None + :type token_storage: Optional[TokenStorage], optional + :param private_key_decryptor: Object responsible for decrypting private key for jwt auth. If no custom implementation provided, the DefaultPrivateKeyDecryptor will be used., defaults to None + :type private_key_decryptor: Optional[PrivateKeyDecryptor], optional + """ + config_json_string: str = read_text_from_file(config_file_path) + return JWTConfig.from_config_json_string( + config_json_string, + token_storage=token_storage, + private_key_decryptor=private_key_decryptor, + ) + + +class BoxJWTAuth(Authentication): + def __init__(self, config: JWTConfig, **kwargs): + """ + :param config: An object containing all JWT configuration to use for authentication + :type config: JWTConfig + """ + super().__init__(**kwargs) + self.config = config + self.token_storage = self.config.token_storage + self.subject_id = ( + self.config.enterprise_id + if not self.config.enterprise_id == None + else self.config.user_id + ) + self.subject_type = ( + 'enterprise' if not self.config.enterprise_id == None else 'user' + ) + + def refresh_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Get new access token using JWT auth. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + if is_browser(): + raise BoxSDKError( + message='JWT auth is not supported in browser environment.' + ) + alg: JwtAlgorithm = ( + self.config.algorithm + if not self.config.algorithm == None + else JwtAlgorithm.RS256 + ) + claims: Dict = { + 'exp': get_epoch_time_in_seconds() + 30, + 'box_sub_type': self.subject_type, + } + jwt_options: JwtSignOptions = JwtSignOptions( + algorithm=alg, + audience='https://api.box.com/oauth2/token', + subject=self.subject_id, + issuer=self.config.client_id, + jwtid=get_uuid(), + keyid=self.config.jwt_key_id, + private_key_decryptor=self.config.private_key_decryptor, + ) + jwt_key: JwtKey = JwtKey( + key=self.config.private_key, passphrase=self.config.private_key_passphrase + ) + assertion: str = create_jwt_assertion(claims, jwt_key, jwt_options) + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_JWT_BEARER, + assertion=assertion, + client_id=self.config.client_id, + client_secret=self.config.client_secret, + ) + self.token_storage.store(token) + return token + + def retrieve_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Get the current access token. If the current access token is expired or not found, this method will attempt to refresh the token. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + old_token: Optional[AccessToken] = self.token_storage.get() + if old_token == None: + new_token: AccessToken = self.refresh_token(network_session=network_session) + return new_token + return old_token + + def retrieve_authorization_header( + self, *, network_session: Optional[NetworkSession] = None + ) -> str: + token: AccessToken = self.retrieve_token(network_session=network_session) + return ''.join(['Bearer ', token.access_token]) + + def with_user_subject( + self, user_id: str, *, token_storage: TokenStorage = None + ) -> 'BoxJWTAuth': + """ + Create a new BoxJWTAuth instance that uses the provided user ID as the subject of the JWT assertion. + + May be one of this application's created App User. Depending on the configured User Access Level, may also be any other App User or Managed User in the enterprise. + + + + + + + + :param user_id: The id of the user to authenticate + :type user_id: str + :param token_storage: Object responsible for storing token in newly created BoxJWTAuth. If no custom implementation provided, the token will be stored in memory., defaults to None + :type token_storage: TokenStorage, optional + """ + if token_storage is None: + token_storage = InMemoryTokenStorage() + new_config: JWTConfig = JWTConfig( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + enterprise_id=None, + user_id=user_id, + jwt_key_id=self.config.jwt_key_id, + private_key=self.config.private_key, + private_key_passphrase=self.config.private_key_passphrase, + token_storage=token_storage, + ) + new_auth: 'BoxJWTAuth' = BoxJWTAuth(config=new_config) + return new_auth + + def with_enterprise_subject( + self, enterprise_id: str, *, token_storage: TokenStorage = None + ) -> 'BoxJWTAuth': + """ + Create a new BoxJWTAuth instance that uses the provided enterprise ID as the subject of the JWT assertion. + :param enterprise_id: The id of the enterprise to authenticate + :type enterprise_id: str + :param token_storage: Object responsible for storing token in newly created BoxJWTAuth. If no custom implementation provided, the token will be stored in memory., defaults to None + :type token_storage: TokenStorage, optional + """ + if token_storage is None: + token_storage = InMemoryTokenStorage() + new_config: JWTConfig = JWTConfig( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + enterprise_id=enterprise_id, + user_id=None, + jwt_key_id=self.config.jwt_key_id, + private_key=self.config.private_key, + private_key_passphrase=self.config.private_key_passphrase, + token_storage=token_storage, + ) + new_auth: 'BoxJWTAuth' = BoxJWTAuth(config=new_config) + return new_auth + + def downscope_token( + self, + scopes: List[str], + *, + resource: Optional[str] = None, + shared_link: Optional[str] = None, + network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Downscope access token to the provided scopes. Returning a new access token with the provided scopes, with the original access token unchanged. + :param scopes: The scope(s) to apply to the resulting token. + :type scopes: List[str] + :param resource: The file or folder to get a downscoped token for. If None and shared_link None, the resulting token will not be scoped down to just a single item. The resource should be a full URL to an item, e.g. https://api.box.com/2.0/files/123456., defaults to None + :type resource: Optional[str], optional + :param shared_link: The shared link to get a downscoped token for. If None and item None, the resulting token will not be scoped down to just a single item., defaults to None + :type shared_link: Optional[str], optional + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.retrieve_token( + network_session=network_session + ) + if token == None: + raise BoxSDKError( + message='No access token is available. Make an API call to retrieve a token before calling this method.' + ) + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + downscoped_token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE, + subject_token=token.access_token, + subject_token_type=PostOAuth2TokenSubjectTokenTypeField.URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN, + resource=resource, + scope=' '.join(scopes), + box_shared_link=shared_link, + ) + return downscoped_token + + def revoke_token(self, *, network_session: Optional[NetworkSession] = None) -> None: + """ + Revoke the current access token and remove it from token storage. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + old_token: Optional[AccessToken] = self.token_storage.get() + if old_token == None: + return None + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + auth_manager.revoke_access_token( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + token=old_token.access_token, + ) + self.token_storage.clear() + return None diff --git a/box_sdk_gen/box/oauth.py b/box_sdk_gen/box/oauth.py new file mode 100644 index 000000000..1c62ead04 --- /dev/null +++ b/box_sdk_gen/box/oauth.py @@ -0,0 +1,250 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.serialization.json import serialize + +from typing import List + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenGrantTypeField + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenSubjectTokenTypeField + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.schemas.access_token import AccessToken + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2Token + +from box_sdk_gen.schemas.post_o_auth_2_revoke import PostOAuth2Revoke + +from box_sdk_gen.managers.authorization import AuthorizationManager + +from box_sdk_gen.box.token_storage import TokenStorage + +from box_sdk_gen.box.token_storage import InMemoryTokenStorage + +from box_sdk_gen.serialization.json import sd_to_url_params + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.serialization.json import SerializedData + + +class OAuthConfig: + def __init__( + self, client_id: str, client_secret: str, *, token_storage: TokenStorage = None + ): + if token_storage is None: + token_storage = InMemoryTokenStorage() + self.client_id = client_id + self.client_secret = client_secret + self.token_storage = token_storage + + +class GetAuthorizeUrlOptions: + def __init__( + self, + *, + client_id: Optional[str] = None, + redirect_uri: Optional[str] = None, + response_type: Optional[str] = None, + state: Optional[str] = None, + scope: Optional[str] = None + ): + """ + :param client_id: Box API key used for identifying the application the user is authenticating with, defaults to None + :type client_id: Optional[str], optional + :param redirect_uri: The URI to which Box redirects the browser after the user has granted or denied the application permission. This URI match one of the redirect URIs in the configuration of your application., defaults to None + :type redirect_uri: Optional[str], optional + :param response_type: The type of response we would like to receive., defaults to None + :type response_type: Optional[str], optional + :param state: A custom string of your choice. Box will pass the same string to the redirect URL when authentication is complete. This parameter can be used to identify a user on redirect, as well as protect against hijacked sessions and other exploits., defaults to None + :type state: Optional[str], optional + :param scope: A space-separated list of application scopes you'd like to authenticate the user for. This defaults to all the scopes configured for the application in its configuration page., defaults to None + :type scope: Optional[str], optional + """ + self.client_id = client_id + self.redirect_uri = redirect_uri + self.response_type = response_type + self.state = state + self.scope = scope + + +class BoxOAuth(Authentication): + def __init__(self, config: OAuthConfig, **kwargs): + """ + :param config: Configuration object of OAuth. + :type config: OAuthConfig + """ + super().__init__(**kwargs) + self.config = config + self.token_storage = self.config.token_storage + + def get_authorize_url(self, *, options: GetAuthorizeUrlOptions = None) -> str: + """ + Get the authorization URL for the app user. + """ + if options is None: + options = GetAuthorizeUrlOptions() + params_map: Dict[str, str] = prepare_params( + { + 'client_id': ( + options.client_id + if not options.client_id == None + else self.config.client_id + ), + 'response_type': ( + options.response_type + if not options.response_type == None + else 'code' + ), + 'redirect_uri': options.redirect_uri, + 'state': options.state, + 'scope': options.scope, + } + ) + return ''.join( + [ + 'https://account.box.com/api/oauth2/authorize?', + sd_to_url_params(serialize(params_map)), + ] + ) + + def get_tokens_authorization_code_grant( + self, + authorization_code: str, + *, + network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Acquires token info using an authorization code. + :param authorization_code: The authorization code to use to get tokens. + :type authorization_code: str + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.AUTHORIZATION_CODE, + code=authorization_code, + client_id=self.config.client_id, + client_secret=self.config.client_secret, + ) + self.token_storage.store(token) + return token + + def retrieve_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Get the current access token. If the current access token is expired or not found, this method will attempt to refresh the token. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.token_storage.get() + if token == None: + raise BoxSDKError( + message='Access and refresh tokens not available. Authenticate before making any API call first.' + ) + return token + + def refresh_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Get a new access token for the platform app user. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + old_token: Optional[AccessToken] = self.token_storage.get() + token_used_for_refresh: Optional[str] = ( + old_token.refresh_token if not old_token == None else None + ) + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.REFRESH_TOKEN, + client_id=self.config.client_id, + client_secret=self.config.client_secret, + refresh_token=token_used_for_refresh, + ) + self.token_storage.store(token) + return token + + def retrieve_authorization_header( + self, *, network_session: Optional[NetworkSession] = None + ) -> str: + token: AccessToken = self.retrieve_token(network_session=network_session) + return ''.join(['Bearer ', token.access_token]) + + def revoke_token(self, *, network_session: Optional[NetworkSession] = None) -> None: + """ + Revoke an active Access Token, effectively logging a user out that has been previously authenticated. + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.token_storage.get() + if token == None: + return None + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + auth_manager.revoke_access_token( + client_id=self.config.client_id, + client_secret=self.config.client_secret, + token=token.access_token, + ) + return None + + def downscope_token( + self, + scopes: List[str], + *, + resource: Optional[str] = None, + shared_link: Optional[str] = None, + network_session: Optional[NetworkSession] = None + ) -> AccessToken: + """ + Downscope access token to the provided scopes. Returning a new access token with the provided scopes, with the original access token unchanged. + :param scopes: The scope(s) to apply to the resulting token. + :type scopes: List[str] + :param resource: The file or folder to get a downscoped token for. If None and shared_link None, the resulting token will not be scoped down to just a single item. The resource should be a full URL to an item, e.g. https://api.box.com/2.0/files/123456., defaults to None + :type resource: Optional[str], optional + :param shared_link: The shared link to get a downscoped token for. If None and item None, the resulting token will not be scoped down to just a single item., defaults to None + :type shared_link: Optional[str], optional + :param network_session: An object to keep network session state, defaults to None + :type network_session: Optional[NetworkSession], optional + """ + token: Optional[AccessToken] = self.retrieve_token( + network_session=network_session + ) + if token == None or token.access_token == None: + raise BoxSDKError(message='No access token is available.') + auth_manager: AuthorizationManager = AuthorizationManager( + network_session=( + network_session if not network_session == None else NetworkSession() + ) + ) + downscoped_token: AccessToken = auth_manager.request_access_token( + PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE, + subject_token=token.access_token, + subject_token_type=PostOAuth2TokenSubjectTokenTypeField.URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN, + resource=resource, + scope=' '.join(scopes), + box_shared_link=shared_link, + ) + return downscoped_token diff --git a/box_sdk_gen/box/token_storage.py b/box_sdk_gen/box/token_storage.py new file mode 100644 index 000000000..5b4d2e4b7 --- /dev/null +++ b/box_sdk_gen/box/token_storage.py @@ -0,0 +1,74 @@ +import shelve +from abc import abstractmethod +from typing import Optional + +from ..schemas.access_token import AccessToken + + +class TokenStorage: + @abstractmethod + def store(self, token: AccessToken) -> None: + pass + + @abstractmethod + def get(self) -> Optional[AccessToken]: + pass + + @abstractmethod + def clear(self) -> None: + pass + + +class InMemoryTokenStorage(TokenStorage): + def __init__(self, token: Optional[AccessToken] = None): + self._token = token + + def store(self, token: AccessToken) -> None: + self._token = token + + def get(self) -> Optional[AccessToken]: + return self._token + + def clear(self) -> None: + self._token = None + + +class FileTokenStorage(TokenStorage): + def __init__(self, filename: str = 'token_storage'): + self.filename = filename + + def store(self, token: AccessToken) -> None: + with shelve.open(self.filename) as file: + file['token'] = token + + def get(self) -> Optional[AccessToken]: + with shelve.open(self.filename) as file: + return file.get('token', None) + + def clear(self) -> None: + with shelve.open(self.filename) as file: + if 'token' in file: + del file['token'] + + +class FileWithInMemoryCacheTokenStorage(TokenStorage): + def __init__(self, filename: str = 'token_storage'): + self.filename = filename + self.cached_token: Optional[AccessToken] = None + + def store(self, token: AccessToken) -> None: + with shelve.open(self.filename) as file: + file['token'] = token + self.cached_token = token + + def get(self) -> Optional[AccessToken]: + if self.cached_token is None: + with shelve.open(self.filename) as file: + self.cached_token = file.get('token', None) + return self.cached_token + + def clear(self) -> None: + with shelve.open(self.filename) as file: + if 'token' in file: + del file['token'] + self.cached_token = None diff --git a/box_sdk_gen/client.py b/box_sdk_gen/client.py new file mode 100644 index 000000000..9d5c34948 --- /dev/null +++ b/box_sdk_gen/client.py @@ -0,0 +1,535 @@ +from typing import Dict + +from box_sdk_gen.managers.authorization import AuthorizationManager + +from box_sdk_gen.managers.files import FilesManager + +from box_sdk_gen.managers.trashed_files import TrashedFilesManager + +from box_sdk_gen.managers.app_item_associations import AppItemAssociationsManager + +from box_sdk_gen.managers.downloads import DownloadsManager + +from box_sdk_gen.managers.uploads import UploadsManager + +from box_sdk_gen.managers.chunked_uploads import ChunkedUploadsManager + +from box_sdk_gen.managers.list_collaborations import ListCollaborationsManager + +from box_sdk_gen.managers.comments import CommentsManager + +from box_sdk_gen.managers.tasks import TasksManager + +from box_sdk_gen.managers.file_versions import FileVersionsManager + +from box_sdk_gen.managers.file_metadata import FileMetadataManager + +from box_sdk_gen.managers.file_classifications import FileClassificationsManager + +from box_sdk_gen.managers.skills import SkillsManager + +from box_sdk_gen.managers.file_watermarks import FileWatermarksManager + +from box_sdk_gen.managers.file_requests import FileRequestsManager + +from box_sdk_gen.managers.folders import FoldersManager + +from box_sdk_gen.managers.trashed_folders import TrashedFoldersManager + +from box_sdk_gen.managers.folder_metadata import FolderMetadataManager + +from box_sdk_gen.managers.folder_classifications import FolderClassificationsManager + +from box_sdk_gen.managers.trashed_items import TrashedItemsManager + +from box_sdk_gen.managers.folder_watermarks import FolderWatermarksManager + +from box_sdk_gen.managers.folder_locks import FolderLocksManager + +from box_sdk_gen.managers.metadata_templates import MetadataTemplatesManager + +from box_sdk_gen.managers.classifications import ClassificationsManager + +from box_sdk_gen.managers.metadata_cascade_policies import ( + MetadataCascadePoliciesManager, +) + +from box_sdk_gen.managers.search import SearchManager + +from box_sdk_gen.managers.user_collaborations import UserCollaborationsManager + +from box_sdk_gen.managers.task_assignments import TaskAssignmentsManager + +from box_sdk_gen.managers.shared_links_files import SharedLinksFilesManager + +from box_sdk_gen.managers.shared_links_folders import SharedLinksFoldersManager + +from box_sdk_gen.managers.web_links import WebLinksManager + +from box_sdk_gen.managers.trashed_web_links import TrashedWebLinksManager + +from box_sdk_gen.managers.shared_links_web_links import SharedLinksWebLinksManager + +from box_sdk_gen.managers.shared_links_app_items import SharedLinksAppItemsManager + +from box_sdk_gen.managers.users import UsersManager + +from box_sdk_gen.managers.session_termination import SessionTerminationManager + +from box_sdk_gen.managers.avatars import AvatarsManager + +from box_sdk_gen.managers.transfer import TransferManager + +from box_sdk_gen.managers.email_aliases import EmailAliasesManager + +from box_sdk_gen.managers.memberships import MembershipsManager + +from box_sdk_gen.managers.invites import InvitesManager + +from box_sdk_gen.managers.groups import GroupsManager + +from box_sdk_gen.managers.webhooks import WebhooksManager + +from box_sdk_gen.managers.events import EventsManager + +from box_sdk_gen.managers.collections import CollectionsManager + +from box_sdk_gen.managers.recent_items import RecentItemsManager + +from box_sdk_gen.managers.retention_policies import RetentionPoliciesManager + +from box_sdk_gen.managers.retention_policy_assignments import ( + RetentionPolicyAssignmentsManager, +) + +from box_sdk_gen.managers.legal_hold_policies import LegalHoldPoliciesManager + +from box_sdk_gen.managers.legal_hold_policy_assignments import ( + LegalHoldPolicyAssignmentsManager, +) + +from box_sdk_gen.managers.file_version_retentions import FileVersionRetentionsManager + +from box_sdk_gen.managers.file_version_legal_holds import FileVersionLegalHoldsManager + +from box_sdk_gen.managers.shield_information_barriers import ( + ShieldInformationBarriersManager, +) + +from box_sdk_gen.managers.shield_information_barrier_reports import ( + ShieldInformationBarrierReportsManager, +) + +from box_sdk_gen.managers.shield_information_barrier_segments import ( + ShieldInformationBarrierSegmentsManager, +) + +from box_sdk_gen.managers.shield_information_barrier_segment_members import ( + ShieldInformationBarrierSegmentMembersManager, +) + +from box_sdk_gen.managers.shield_information_barrier_segment_restrictions import ( + ShieldInformationBarrierSegmentRestrictionsManager, +) + +from box_sdk_gen.managers.device_pinners import DevicePinnersManager + +from box_sdk_gen.managers.terms_of_services import TermsOfServicesManager + +from box_sdk_gen.managers.terms_of_service_user_statuses import ( + TermsOfServiceUserStatusesManager, +) + +from box_sdk_gen.managers.collaboration_allowlist_entries import ( + CollaborationAllowlistEntriesManager, +) + +from box_sdk_gen.managers.collaboration_allowlist_exempt_targets import ( + CollaborationAllowlistExemptTargetsManager, +) + +from box_sdk_gen.managers.storage_policies import StoragePoliciesManager + +from box_sdk_gen.managers.storage_policy_assignments import ( + StoragePolicyAssignmentsManager, +) + +from box_sdk_gen.managers.zip_downloads import ZipDownloadsManager + +from box_sdk_gen.managers.sign_requests import SignRequestsManager + +from box_sdk_gen.managers.workflows import WorkflowsManager + +from box_sdk_gen.managers.sign_templates import SignTemplatesManager + +from box_sdk_gen.managers.integration_mappings import IntegrationMappingsManager + +from box_sdk_gen.managers.ai import AiManager + +from box_sdk_gen.managers.ai_studio import AiStudioManager + +from box_sdk_gen.managers.docgen_template import DocgenTemplateManager + +from box_sdk_gen.managers.docgen import DocgenManager + +from box_sdk_gen.managers.hubs import HubsManager + +from box_sdk_gen.managers.hub_collaborations import HubCollaborationsManager + +from box_sdk_gen.managers.hub_items import HubItemsManager + +from box_sdk_gen.managers.shield_lists import ShieldListsManager + +from box_sdk_gen.managers.archives import ArchivesManager + +from box_sdk_gen.managers.external_users import ExternalUsersManager + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.networking.base_urls import BaseUrls + +from box_sdk_gen.networking.proxy_config import ProxyConfig + + +class BoxClient: + def __init__(self, auth: Authentication, *, network_session: NetworkSession = None): + if network_session is None: + network_session = NetworkSession(base_urls=BaseUrls()) + self.auth = auth + self.network_session = network_session + self.authorization = AuthorizationManager( + auth=self.auth, network_session=self.network_session + ) + self.files = FilesManager(auth=self.auth, network_session=self.network_session) + self.trashed_files = TrashedFilesManager( + auth=self.auth, network_session=self.network_session + ) + self.app_item_associations = AppItemAssociationsManager( + auth=self.auth, network_session=self.network_session + ) + self.downloads = DownloadsManager( + auth=self.auth, network_session=self.network_session + ) + self.uploads = UploadsManager( + auth=self.auth, network_session=self.network_session + ) + self.chunked_uploads = ChunkedUploadsManager( + auth=self.auth, network_session=self.network_session + ) + self.list_collaborations = ListCollaborationsManager( + auth=self.auth, network_session=self.network_session + ) + self.comments = CommentsManager( + auth=self.auth, network_session=self.network_session + ) + self.tasks = TasksManager(auth=self.auth, network_session=self.network_session) + self.file_versions = FileVersionsManager( + auth=self.auth, network_session=self.network_session + ) + self.file_metadata = FileMetadataManager( + auth=self.auth, network_session=self.network_session + ) + self.file_classifications = FileClassificationsManager( + auth=self.auth, network_session=self.network_session + ) + self.skills = SkillsManager( + auth=self.auth, network_session=self.network_session + ) + self.file_watermarks = FileWatermarksManager( + auth=self.auth, network_session=self.network_session + ) + self.file_requests = FileRequestsManager( + auth=self.auth, network_session=self.network_session + ) + self.folders = FoldersManager( + auth=self.auth, network_session=self.network_session + ) + self.trashed_folders = TrashedFoldersManager( + auth=self.auth, network_session=self.network_session + ) + self.folder_metadata = FolderMetadataManager( + auth=self.auth, network_session=self.network_session + ) + self.folder_classifications = FolderClassificationsManager( + auth=self.auth, network_session=self.network_session + ) + self.trashed_items = TrashedItemsManager( + auth=self.auth, network_session=self.network_session + ) + self.folder_watermarks = FolderWatermarksManager( + auth=self.auth, network_session=self.network_session + ) + self.folder_locks = FolderLocksManager( + auth=self.auth, network_session=self.network_session + ) + self.metadata_templates = MetadataTemplatesManager( + auth=self.auth, network_session=self.network_session + ) + self.classifications = ClassificationsManager( + auth=self.auth, network_session=self.network_session + ) + self.metadata_cascade_policies = MetadataCascadePoliciesManager( + auth=self.auth, network_session=self.network_session + ) + self.search = SearchManager( + auth=self.auth, network_session=self.network_session + ) + self.user_collaborations = UserCollaborationsManager( + auth=self.auth, network_session=self.network_session + ) + self.task_assignments = TaskAssignmentsManager( + auth=self.auth, network_session=self.network_session + ) + self.shared_links_files = SharedLinksFilesManager( + auth=self.auth, network_session=self.network_session + ) + self.shared_links_folders = SharedLinksFoldersManager( + auth=self.auth, network_session=self.network_session + ) + self.web_links = WebLinksManager( + auth=self.auth, network_session=self.network_session + ) + self.trashed_web_links = TrashedWebLinksManager( + auth=self.auth, network_session=self.network_session + ) + self.shared_links_web_links = SharedLinksWebLinksManager( + auth=self.auth, network_session=self.network_session + ) + self.shared_links_app_items = SharedLinksAppItemsManager( + auth=self.auth, network_session=self.network_session + ) + self.users = UsersManager(auth=self.auth, network_session=self.network_session) + self.session_termination = SessionTerminationManager( + auth=self.auth, network_session=self.network_session + ) + self.avatars = AvatarsManager( + auth=self.auth, network_session=self.network_session + ) + self.transfer = TransferManager( + auth=self.auth, network_session=self.network_session + ) + self.email_aliases = EmailAliasesManager( + auth=self.auth, network_session=self.network_session + ) + self.memberships = MembershipsManager( + auth=self.auth, network_session=self.network_session + ) + self.invites = InvitesManager( + auth=self.auth, network_session=self.network_session + ) + self.groups = GroupsManager( + auth=self.auth, network_session=self.network_session + ) + self.webhooks = WebhooksManager( + auth=self.auth, network_session=self.network_session + ) + self.events = EventsManager( + auth=self.auth, network_session=self.network_session + ) + self.collections = CollectionsManager( + auth=self.auth, network_session=self.network_session + ) + self.recent_items = RecentItemsManager( + auth=self.auth, network_session=self.network_session + ) + self.retention_policies = RetentionPoliciesManager( + auth=self.auth, network_session=self.network_session + ) + self.retention_policy_assignments = RetentionPolicyAssignmentsManager( + auth=self.auth, network_session=self.network_session + ) + self.legal_hold_policies = LegalHoldPoliciesManager( + auth=self.auth, network_session=self.network_session + ) + self.legal_hold_policy_assignments = LegalHoldPolicyAssignmentsManager( + auth=self.auth, network_session=self.network_session + ) + self.file_version_retentions = FileVersionRetentionsManager( + auth=self.auth, network_session=self.network_session + ) + self.file_version_legal_holds = FileVersionLegalHoldsManager( + auth=self.auth, network_session=self.network_session + ) + self.shield_information_barriers = ShieldInformationBarriersManager( + auth=self.auth, network_session=self.network_session + ) + self.shield_information_barrier_reports = ( + ShieldInformationBarrierReportsManager( + auth=self.auth, network_session=self.network_session + ) + ) + self.shield_information_barrier_segments = ( + ShieldInformationBarrierSegmentsManager( + auth=self.auth, network_session=self.network_session + ) + ) + self.shield_information_barrier_segment_members = ( + ShieldInformationBarrierSegmentMembersManager( + auth=self.auth, network_session=self.network_session + ) + ) + self.shield_information_barrier_segment_restrictions = ( + ShieldInformationBarrierSegmentRestrictionsManager( + auth=self.auth, network_session=self.network_session + ) + ) + self.device_pinners = DevicePinnersManager( + auth=self.auth, network_session=self.network_session + ) + self.terms_of_services = TermsOfServicesManager( + auth=self.auth, network_session=self.network_session + ) + self.terms_of_service_user_statuses = TermsOfServiceUserStatusesManager( + auth=self.auth, network_session=self.network_session + ) + self.collaboration_allowlist_entries = CollaborationAllowlistEntriesManager( + auth=self.auth, network_session=self.network_session + ) + self.collaboration_allowlist_exempt_targets = ( + CollaborationAllowlistExemptTargetsManager( + auth=self.auth, network_session=self.network_session + ) + ) + self.storage_policies = StoragePoliciesManager( + auth=self.auth, network_session=self.network_session + ) + self.storage_policy_assignments = StoragePolicyAssignmentsManager( + auth=self.auth, network_session=self.network_session + ) + self.zip_downloads = ZipDownloadsManager( + auth=self.auth, network_session=self.network_session + ) + self.sign_requests = SignRequestsManager( + auth=self.auth, network_session=self.network_session + ) + self.workflows = WorkflowsManager( + auth=self.auth, network_session=self.network_session + ) + self.sign_templates = SignTemplatesManager( + auth=self.auth, network_session=self.network_session + ) + self.integration_mappings = IntegrationMappingsManager( + auth=self.auth, network_session=self.network_session + ) + self.ai = AiManager(auth=self.auth, network_session=self.network_session) + self.ai_studio = AiStudioManager( + auth=self.auth, network_session=self.network_session + ) + self.docgen_template = DocgenTemplateManager( + auth=self.auth, network_session=self.network_session + ) + self.docgen = DocgenManager( + auth=self.auth, network_session=self.network_session + ) + self.hubs = HubsManager(auth=self.auth, network_session=self.network_session) + self.hub_collaborations = HubCollaborationsManager( + auth=self.auth, network_session=self.network_session + ) + self.hub_items = HubItemsManager( + auth=self.auth, network_session=self.network_session + ) + self.shield_lists = ShieldListsManager( + auth=self.auth, network_session=self.network_session + ) + self.archives = ArchivesManager( + auth=self.auth, network_session=self.network_session + ) + self.external_users = ExternalUsersManager( + auth=self.auth, network_session=self.network_session + ) + + def make_request(self, fetch_options: FetchOptions) -> FetchResponse: + """ + Make a custom http request using the client authentication and network session. + :param fetch_options: Options to be passed to the fetch call + :type fetch_options: FetchOptions + """ + auth: Authentication = ( + self.auth if fetch_options.auth == None else fetch_options.auth + ) + network_session: NetworkSession = ( + self.network_session + if fetch_options.network_session == None + else fetch_options.network_session + ) + enriched_fetch_options: FetchOptions = FetchOptions( + auth=auth, + network_session=network_session, + url=fetch_options.url, + method=fetch_options.method, + params=fetch_options.params, + headers=fetch_options.headers, + data=fetch_options.data, + file_stream=fetch_options.file_stream, + multipart_data=fetch_options.multipart_data, + content_type=fetch_options.content_type, + response_format=fetch_options.response_format, + follow_redirects=fetch_options.follow_redirects, + ) + return network_session.network_client.fetch(enriched_fetch_options) + + def with_as_user_header(self, user_id: str) -> 'BoxClient': + """ + Create a new client to impersonate user with the provided ID. All calls made with the new client will be made in context of the impersonated user, leaving the original client unmodified. + :param user_id: ID of an user to impersonate + :type user_id: str + """ + return BoxClient( + auth=self.auth, + network_session=self.network_session.with_additional_headers( + {'As-User': user_id} + ), + ) + + def with_suppressed_notifications(self) -> 'BoxClient': + """ + Create a new client with suppressed notifications. Calls made with the new client will not trigger email or webhook notifications + """ + return BoxClient( + auth=self.auth, + network_session=self.network_session.with_additional_headers( + {'Box-Notifications': 'off'} + ), + ) + + def with_extra_headers( + self, *, extra_headers: Dict[str, str] = None + ) -> 'BoxClient': + """ + Create a new client with a custom set of headers that will be included in every API call + :param extra_headers: Custom set of headers that will be included in every API call, defaults to None + :type extra_headers: Dict[str, str], optional + """ + if extra_headers is None: + extra_headers = {} + return BoxClient( + auth=self.auth, + network_session=self.network_session.with_additional_headers(extra_headers), + ) + + def with_custom_base_urls(self, base_urls: BaseUrls) -> 'BoxClient': + """ + Create a new client with a custom set of base urls that will be used for every API call + :param base_urls: Custom set of base urls that will be used for every API call + :type base_urls: BaseUrls + """ + return BoxClient( + auth=self.auth, + network_session=self.network_session.with_custom_base_urls(base_urls), + ) + + def with_proxy(self, config: ProxyConfig) -> 'BoxClient': + """ + Create a new client with a custom proxy that will be used for every API call + """ + return BoxClient( + auth=self.auth, network_session=self.network_session.with_proxy(config) + ) diff --git a/box_sdk_gen/internal/__init__.py b/box_sdk_gen/internal/__init__.py new file mode 100644 index 000000000..af3032fc6 --- /dev/null +++ b/box_sdk_gen/internal/__init__.py @@ -0,0 +1,9 @@ +from box_sdk_gen.internal.utils import * + +from box_sdk_gen.internal.logging import * + +from box_sdk_gen.internal.errors import * + +from box_sdk_gen.internal.base_object import * + +from box_sdk_gen.internal.null_value import * diff --git a/box_sdk_gen/internal/base_object.py b/box_sdk_gen/internal/base_object.py new file mode 100644 index 000000000..47c407323 --- /dev/null +++ b/box_sdk_gen/internal/base_object.py @@ -0,0 +1,165 @@ +from datetime import datetime, date +from enum import EnumMeta, Enum +from typing import get_args, get_origin, Union, Optional +from .null_value import NullValue + + +class BaseObject: + _discriminator = (None, {}) + _json_to_fields_mapping = {} + _fields_to_json_mapping = {} + + def __init__(self, **kwargs): + self._raw_data: dict = {} + self.__dict__.update(kwargs) + + @classmethod + def from_dict(cls, data: dict): + unpacked_attributes = {} + for key, value in data.items(): + mapping_field_name = cls._json_to_fields_mapping.get(key, key) + annotation = cls.__init__.__annotations__.get(mapping_field_name, None) + unpacked_attributes[mapping_field_name] = cls._deserialize( + key, value, annotation + ) + return cls(**unpacked_attributes) + + @property + def raw_data(self): + """ + Returns the raw json representation returned by the API + :return: dict with the raw json data + """ + return self._raw_data + + def to_dict(self) -> dict: + result_dict = {} + for k, v in vars(self).items(): + # Skip private and protected attributes + if k.startswith("_"): + continue + if v is None: + continue + if isinstance(v, NullValue): + value = None + elif type(v) is list: + value = [ + item.to_dict() if isinstance(item, BaseObject) else item + for item in v + ] + elif type(v) is dict: + value = { + key: value.to_dict() if isinstance(value, BaseObject) else value + for key, value in v.items() + } + elif isinstance(v, BaseObject): + value = v.to_dict() + elif isinstance(v, Enum): + value = v.value + elif isinstance(v, date): + value = v.isoformat() + elif isinstance(v, datetime): + value = v.isoformat().replace('+00:00', 'Z') + else: + value = v + result_dict[self._fields_to_json_mapping.get(k, k)] = value + + return result_dict + + @classmethod + def _deserialize(cls, key, value, annotation=None): + if annotation is None or value is None: + return value + if get_origin(annotation) == Optional: + return cls._deserialize(key, value, get_args(annotation)) + if get_origin(annotation) == Union: + union_without_none_type = [ + arg for arg in get_args(annotation) if arg is not type(None) + ] + if len(union_without_none_type) == 1: + return cls._deserialize(key, value, union_without_none_type[0]) + + if get_origin(annotation) == list: + return cls._deserialize_list(key, value, annotation) + elif get_origin(annotation) == Union: + return cls._deserialize_union(key, value, annotation) + elif isinstance(annotation, EnumMeta): + return cls._deserialize_enum(key, value, annotation) + elif annotation == datetime: + return cls._deserialize_datetime(key, value, annotation) + elif annotation == date: + return cls._deserialize_date(key, value, annotation) + elif isinstance(annotation, type) and issubclass(annotation, BaseObject): + return cls._deserialize_nested_type(key, value, annotation) + else: + return value + + @classmethod + def _deserialize_list(cls, key, value, annotation: list): + try: + list_type = get_args(annotation)[0] + return [ + cls._deserialize(key, list_entry, list_type) for list_entry in value + ] + except Exception: + return value + + @classmethod + def _deserialize_union(cls, key, value, annotation): + try: + possible_types = get_args(annotation) + if value is None: + if type(None) not in possible_types: + print( + 'Value: ', value, 'should not be allowed in Union:', annotation + ) + return value + + for possible_type in possible_types: + if ( + isinstance(possible_type, type) + and issubclass(possible_type, BaseObject) + and value.get(possible_type._discriminator[0], None) + in possible_type._discriminator[1] + ): + return cls._deserialize(key, value, possible_type) + + for possible_type in possible_types: + try: + return cls._deserialize(key, value, possible_type) + except Exception: + continue + return value + except Exception: + return value + + @classmethod + def _deserialize_enum(cls, key, value, annotation): + try: + return getattr(annotation, value.upper().replace(' ', '_')) + except Exception: + return value + + @classmethod + def _deserialize_datetime(cls, key, value, annotation): + try: + return datetime.fromisoformat(value.replace('Z', '+00:00')) + except Exception: + return value + + @classmethod + def _deserialize_date(cls, key, value, annotation): + try: + return date.fromisoformat(value) + except Exception: + return value + + @classmethod + def _deserialize_nested_type(cls, key, value, annotation): + try: + return annotation.from_dict(value) + except Exception: + return value + + def __repr__(self) -> str: + return f'{self.__class__} {self.to_dict()}' diff --git a/box_sdk_gen/internal/errors.py b/box_sdk_gen/internal/errors.py new file mode 100644 index 000000000..856fd6a38 --- /dev/null +++ b/box_sdk_gen/internal/errors.py @@ -0,0 +1,6 @@ +class GeneratedCodeError(Exception): + def __init__(self, message: str, **kwargs): + super().__init__(message) + self.name = 'GeneratedCodeError' + self.message = message + self.extra = kwargs diff --git a/box_sdk_gen/internal/logging.py b/box_sdk_gen/internal/logging.py new file mode 100644 index 000000000..41371aa47 --- /dev/null +++ b/box_sdk_gen/internal/logging.py @@ -0,0 +1,31 @@ +from typing import Dict + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import sanitize_map + +from box_sdk_gen.serialization.json import sanitize_serialized_data + + +class DataSanitizer: + def __init__(self): + self._keys_to_sanitize = { + 'authorization': '', + 'access_token': '', + 'refresh_token': '', + 'subject_token': '', + 'token': '', + 'client_id': '', + 'client_secret': '', + 'shared_link': '', + 'download_url': '', + 'jwt_private_key': '', + 'jwt_private_key_passphrase': '', + 'password': '', + } + + def sanitize_headers(self, headers: Dict[str, str]) -> Dict[str, str]: + return sanitize_map(headers, self._keys_to_sanitize) + + def sanitize_body(self, body: SerializedData) -> SerializedData: + return sanitize_serialized_data(body, self._keys_to_sanitize) diff --git a/box_sdk_gen/internal/null_value.py b/box_sdk_gen/internal/null_value.py new file mode 100644 index 000000000..9001c9a06 --- /dev/null +++ b/box_sdk_gen/internal/null_value.py @@ -0,0 +1,5 @@ +class NullValue: + pass + + +null = NullValue() diff --git a/box_sdk_gen/internal/utils.py b/box_sdk_gen/internal/utils.py new file mode 100644 index 000000000..9289a03ca --- /dev/null +++ b/box_sdk_gen/internal/utils.py @@ -0,0 +1,501 @@ +import base64 +import datetime +import hashlib +import os +import re +import shutil +import uuid +import time +import hmac +from random import uniform +from enum import Enum +from io import SEEK_CUR, SEEK_END, SEEK_SET, BufferedIOBase, BytesIO +from typing import Any, Callable, Dict, Iterable, Optional, TypeVar, BinaryIO + +from abc import abstractmethod +from typing import Any + +try: + import jwt + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import serialization +except ImportError: + jwt, default_backend, serialization = None, None, None + +from .base_object import BaseObject +from ..serialization.json import sd_to_json, sanitized_value +from ..serialization.json import serialize +from .null_value import null + +ByteStream = BufferedIOBase +OutputStream = BinaryIO +Buffer = bytes + + +class ResponseByteStream(ByteStream): + def __init__(self, request_iterator): + self._iterator = request_iterator + self._buffer = b'' + self._position = 0 + self._eos = False + + def _read_from_iterator(self, size): + """ + Read up to `size` bytes from the iterator into the buffer + :param size: Number of bytes to read. If None, read the entire stream. + """ + if self._eos: + return + + while len(self._buffer) < size: + try: + chunk = next(self._iterator) + self._buffer += chunk + except StopIteration: + self._eos = True + break + + def tell(self): + """ + Returns the current position in the stream + :return: + """ + return self._position + + def read(self, size=None): + """ + Reads up to `size` bytes from the stream + :param size: Read up to `size` bytes from the stream. If None read the entire stream. + :return: Bytes read from the stream + """ + if size is None: + # Read everything remaining in the stream. + result = self._buffer + b''.join(self._iterator) + self._buffer = b'' + self._position += len(result) + self._eos = True + return result + + self._read_from_iterator(size) + result = self._buffer[:size] + self._buffer = self._buffer[size:] + self._position += len(result) + return result + + def seek(self, position, whence=SEEK_SET): + """ + Move the stream to given position + :param position: Position to move to + :param whence: One of SEEK_SET = 0, SEEK_CUR = 1 or SEEK_END = 2 + :return: The new position in the stream + """ + if whence == SEEK_SET: + if position < self._position: + raise ValueError('Cannot seek backwards in a stream') + self.read(position - self._position) + elif whence == SEEK_CUR: + self.read(position) + elif whence == SEEK_END: + raise NotImplementedError('SEEK_END is not supported for streams') + else: + raise ValueError('Invalid value for `whence`') + + return self._position + + +def get_env_var(name: str) -> str: + return os.getenv(name) + + +def get_uuid() -> str: + return str(uuid.uuid1()) + + +def decode_base_64(value: str) -> str: + return base64.b64decode(value).decode() + + +def generate_byte_buffer(size: int) -> Buffer: + return Buffer(os.urandom(size)) + + +def generate_byte_stream_from_buffer(buffer: Buffer) -> ByteStream: + return BytesIO(buffer) + + +def generate_byte_stream(size: int) -> ByteStream: + return BytesIO(os.urandom(size)) + + +def buffer_equals(buffer1: Buffer, buffer2: Buffer) -> bool: + return buffer1 == buffer2 + + +def buffer_length(buffer: Buffer) -> int: + return len(buffer) + + +def decode_base_64_byte_stream(value: str) -> ByteStream: + return BytesIO(base64.b64decode(value)) + + +def string_to_byte_stream(value: str) -> ByteStream: + return BytesIO(bytes(value, 'utf-8')) + + +def read_byte_stream(byte_stream: ByteStream) -> Buffer: + return Buffer(byte_stream.read()) + + +def write_input_stream_to_output_stream( + input_stream: ByteStream, output_stream: OutputStream +): + shutil.copyfileobj(input_stream, output_stream) + + +def get_file_output_stream(file_path: str) -> OutputStream: + return open(file_path, 'wb') + + +def close_file_output_stream(file_output_stream: OutputStream): + file_output_stream.close() + + +def read_buffer_from_file(path: str) -> bytes: + with open(path, 'rb') as file: + return file.read() + + +def prepare_params(map: Dict[str, Optional[str]]) -> Dict[str, str]: + return {k: v for k, v in map.items() if v is not None} + + +def to_string(value: Any) -> Optional[str]: + if value is None: + return None + if isinstance(value, datetime.datetime): + return date_time_to_string(value) + if isinstance(value, datetime.date): + return date_to_string(value) + if ( + isinstance(value, BaseObject) + or isinstance(value, list) + and len(value) >= 1 + and isinstance(value[0], BaseObject) + ): + return ''.join(sd_to_json(serialize(value)).split()) + if isinstance(value, list): + return ','.join(map(to_string, value)) + if isinstance(value, Enum): + return value.value + return str(value) + + +class HashName(str, Enum): + SHA1 = 'sha1' + + +class Hash: + def __init__(self, algorithm: HashName): + self.algorithm = algorithm + self.hash = hashlib.sha1() + + def update_hash(self, data: Buffer): + self.hash.update(data) + + def digest_hash(self, encoding): + return base64.b64encode(self.hash.digest()).decode("utf-8") + + +def hex_to_base_64(data: hex): + return base64.b64encode(bytes.fromhex(data)).decode() + + +T = TypeVar('T') +Iterator = Iterable[T] +Accumulator = TypeVar('Accumulator') + + +def iterate_chunks( + stream: ByteStream, chunk_size: int, file_size: int +) -> Iterable[ByteStream]: + stream_is_finished = False + while not stream_is_finished: + copied_length = 0 + chunk = b'' + while copied_length < chunk_size: + bytes_read = stream.read(chunk_size - copied_length) + if bytes_read is None: + # stream returns none when no bytes are ready currently but there are + # potentially more bytes in the stream to be read. + continue + if not bytes_read: + # stream is exhausted. + stream_is_finished = True + break + chunk += bytes_read + copied_length += len(bytes_read) + if chunk: + yield BytesIO(chunk) + + +def reduce_iterator( + iterator: Iterator, + reducer: Callable[[Accumulator, T], Accumulator], + initial_value: Accumulator, +) -> Accumulator: + result = initial_value + + for item in iterator: + result = reducer(result, item) + + return result + + +def read_text_from_file(file_path: str) -> str: + with open(file_path, 'r') as file: + return file.read() + + +def is_browser() -> bool: + return False + + +def get_epoch_time_in_seconds() -> int: + return int(time.time()) + + +def get_value_from_object_raw_data(obj: BaseObject, key: str) -> Any: + keys = key.split('.') + value: dict = obj.raw_data + for k in keys: + value = value.get(k, {}) + return value + + +class PrivateKeyDecryptor: + """Class used for private key decryption in JWT auth.""" + + @abstractmethod + def decrypt_private_key(self, encryptedPrivateKey: str, passphrase: str) -> Any: + """Decrypts private key using a passphrase.""" + pass + + +class DefaultPrivateKeyDecryptor(PrivateKeyDecryptor): + def decrypt_private_key(self, encryptedPrivateKey: str, passphrase: str) -> Any: + if default_backend is None or serialization is None: + raise ImportError( + 'Missing `cryptography` dependency. `cryptography` library is required to create JWT assertion.' + ) + encoded_private_key = encode_str_ascii_or_raise(encryptedPrivateKey) + encoded_passphrase = encode_str_ascii_or_raise(passphrase) + + return serialization.load_pem_private_key( + encoded_private_key, + password=encoded_passphrase, + backend=default_backend(), + ) + + +class JwtAlgorithm(str, Enum): + HS256 = 'HS256' + HS384 = 'HS384' + HS512 = 'HS512' + RS256 = 'RS256' + RS384 = 'RS384' + RS512 = 'RS512' + ES256 = 'ES256' + ES384 = 'ES384' + ES512 = 'ES512' + PS256 = 'PS256' + PS384 = 'PS384' + PS512 = 'PS512' + none = 'none' + + +class JwtSignOptions(BaseObject): + def __init__( + self, + algorithm: JwtAlgorithm, + headers: Dict[str, str] = None, + audience: Optional[str] = None, + issuer: Optional[str] = None, + subject: Optional[str] = None, + jwtid: Optional[str] = None, + keyid: Optional[str] = None, + private_key_decryptor: Optional[PrivateKeyDecryptor] = None, + **kwargs, + ): + super().__init__(**kwargs) + if headers is None: + headers = {} + self.algorithm = algorithm + self.headers = headers + self.audience = audience + self.issuer = issuer + self.subject = subject + self.jwtid = jwtid + self.keyid = keyid + self.private_key_decryptor = ( + private_key_decryptor + if private_key_decryptor is not None + else DefaultPrivateKeyDecryptor() + ) + + +class JwtKey(BaseObject): + def __init__(self, key: str, passphrase: str, **kwargs): + super().__init__(**kwargs) + self.key = key + self.passphrase = passphrase + + +def encode_str_ascii_or_raise(passphrase: str) -> bytes: + try: + return passphrase.encode('ascii') + except UnicodeError as unicode_error: + raise TypeError( + "private_key and private_key_passphrase must contain binary data (bytes/str), not a text/unicode string" + ) from unicode_error + + +def create_jwt_assertion(claims: dict, key: JwtKey, options: JwtSignOptions) -> str: + if jwt is None: + raise ImportError( + 'Missing `PyJWT` dependency. `PyJWT` library is required to create JWT assertion.' + ) + return jwt.encode( + { + 'iss': options.issuer, + 'sub': options.subject, + 'box_sub_type': claims['box_sub_type'], + 'aud': options.audience, + 'jti': options.jwtid, + 'exp': claims['exp'], + }, + options.private_key_decryptor.decrypt_private_key(key.key, key.passphrase), + algorithm=options.algorithm, + headers={'kid': options.keyid}, + ) + + +Date = datetime.date +DateTime = datetime.datetime + + +def date_to_string(date: Date) -> str: + return date.isoformat() + + +def date_from_string(date: str) -> Date: + return Date.fromisoformat(date) + + +def date_time_to_string(date_time: DateTime) -> str: + return date_time.isoformat().replace('+00:00', 'Z') + + +def date_time_from_string(date_time: str) -> DateTime: + return DateTime.fromisoformat(date_time.replace('Z', '+00:00')) + + +def date_time_to_epoch_seconds(date_time: DateTime) -> int: + return int(date_time.timestamp()) + + +def epoch_seconds_to_date_time(epoch_seconds: int) -> DateTime: + return DateTime.fromtimestamp(epoch_seconds, datetime.timezone.utc) + + +def delay_in_seconds(seconds: int): + time.sleep(seconds) + + +def create_null(): + return null + + +def escape_unicode(value: str) -> str: + def replace_char(match): + char = match.group(0) + code_point = ord(char) + if char == '\n': + return '\\n' + elif char == '\r': + return '\\r' + elif char == '\t': + return '\\t' + elif code_point <= 0xFFFF: # Basic Multilingual Plane (BMP) + return f"\\u{code_point:04x}" + else: # Supplementary Plane (Surrogate Pair) + code_point -= 0x10000 + high_surrogate = 0xD800 + (code_point >> 10) + low_surrogate = 0xDC00 + (code_point & 0x3FF) + return f"\\u{high_surrogate:04x}\\u{low_surrogate:04x}" + + # Replace any backslashes that are NOT part of a \/ with double backslash + temp = re.sub(r'\\(?!/)', r'\\\\', value) + + # Match special characters, non-ASCII characters + return re.sub(r'[^\x20-\x7e]|[\n\r\t]', replace_char, temp) + + +def compute_webhook_signature( + body: str, + headers: Dict[str, str], + signature_key: str, + escape_body: Optional[bool] = False, +) -> Optional[str]: + """ + Computes the Hmac for the webhook notification given one signature key. + + :param body: + The encoded webhook body. + :param headers: + The headers for the `Webhook` notification. + :param signature_key: + The `Webhook` signature key for this application. + :param escape_body: + Indicates if payload should be escaped or left as is. + :return: + An Hmac signature. + """ + if signature_key is None: + return None + if headers.get('box-signature-version') != '1': + return None + if headers.get('box-signature-algorithm') != 'HmacSHA256': + return None + + encoded_body = (escape_unicode(body) if escape_body else body).encode('utf-8') + encoded_signature_key = signature_key.encode('utf-8') + encoded_delivery_time_stamp = headers.get('box-delivery-timestamp').encode('utf-8') + new_hmac = hmac.new(encoded_signature_key, digestmod=hashlib.sha256) + new_hmac.update(encoded_body) + new_hmac.update(encoded_delivery_time_stamp) + signature = base64.b64encode(new_hmac.digest()).decode() + return signature + + +def compare_signatures( + expected_signature: Optional[str], received_signature: Optional[str] +) -> bool: + if not expected_signature or not received_signature: + return False + if len(expected_signature) != len(received_signature): + return False + return hmac.compare_digest(expected_signature, received_signature) + + +def random(min: float, max: float) -> float: + return uniform(min, max) + + +def sanitize_map( + dictionary: Dict[str, str], keys_to_sanitize: Dict[str, str] +) -> Dict[str, str]: + return { + k: sanitized_value() if k.lower() in keys_to_sanitize else v + for k, v in dictionary.items() + } diff --git a/box_sdk_gen/managers/__init__.py b/box_sdk_gen/managers/__init__.py new file mode 100644 index 000000000..f3a23b7fb --- /dev/null +++ b/box_sdk_gen/managers/__init__.py @@ -0,0 +1,159 @@ +from box_sdk_gen.managers.authorization import * + +from box_sdk_gen.managers.files import * + +from box_sdk_gen.managers.trashed_files import * + +from box_sdk_gen.managers.app_item_associations import * + +from box_sdk_gen.managers.downloads import * + +from box_sdk_gen.managers.uploads import * + +from box_sdk_gen.managers.chunked_uploads import * + +from box_sdk_gen.managers.list_collaborations import * + +from box_sdk_gen.managers.comments import * + +from box_sdk_gen.managers.tasks import * + +from box_sdk_gen.managers.file_versions import * + +from box_sdk_gen.managers.file_metadata import * + +from box_sdk_gen.managers.file_classifications import * + +from box_sdk_gen.managers.skills import * + +from box_sdk_gen.managers.file_watermarks import * + +from box_sdk_gen.managers.file_requests import * + +from box_sdk_gen.managers.folders import * + +from box_sdk_gen.managers.trashed_folders import * + +from box_sdk_gen.managers.folder_metadata import * + +from box_sdk_gen.managers.folder_classifications import * + +from box_sdk_gen.managers.trashed_items import * + +from box_sdk_gen.managers.folder_watermarks import * + +from box_sdk_gen.managers.folder_locks import * + +from box_sdk_gen.managers.metadata_templates import * + +from box_sdk_gen.managers.classifications import * + +from box_sdk_gen.managers.metadata_cascade_policies import * + +from box_sdk_gen.managers.search import * + +from box_sdk_gen.managers.user_collaborations import * + +from box_sdk_gen.managers.task_assignments import * + +from box_sdk_gen.managers.shared_links_files import * + +from box_sdk_gen.managers.shared_links_folders import * + +from box_sdk_gen.managers.web_links import * + +from box_sdk_gen.managers.trashed_web_links import * + +from box_sdk_gen.managers.shared_links_web_links import * + +from box_sdk_gen.managers.shared_links_app_items import * + +from box_sdk_gen.managers.users import * + +from box_sdk_gen.managers.session_termination import * + +from box_sdk_gen.managers.avatars import * + +from box_sdk_gen.managers.transfer import * + +from box_sdk_gen.managers.email_aliases import * + +from box_sdk_gen.managers.memberships import * + +from box_sdk_gen.managers.invites import * + +from box_sdk_gen.managers.groups import * + +from box_sdk_gen.managers.webhooks import * + +from box_sdk_gen.managers.events import * + +from box_sdk_gen.managers.collections import * + +from box_sdk_gen.managers.recent_items import * + +from box_sdk_gen.managers.retention_policies import * + +from box_sdk_gen.managers.retention_policy_assignments import * + +from box_sdk_gen.managers.legal_hold_policies import * + +from box_sdk_gen.managers.legal_hold_policy_assignments import * + +from box_sdk_gen.managers.file_version_retentions import * + +from box_sdk_gen.managers.file_version_legal_holds import * + +from box_sdk_gen.managers.shield_information_barriers import * + +from box_sdk_gen.managers.shield_information_barrier_reports import * + +from box_sdk_gen.managers.shield_information_barrier_segments import * + +from box_sdk_gen.managers.shield_information_barrier_segment_members import * + +from box_sdk_gen.managers.shield_information_barrier_segment_restrictions import * + +from box_sdk_gen.managers.device_pinners import * + +from box_sdk_gen.managers.terms_of_services import * + +from box_sdk_gen.managers.terms_of_service_user_statuses import * + +from box_sdk_gen.managers.collaboration_allowlist_entries import * + +from box_sdk_gen.managers.collaboration_allowlist_exempt_targets import * + +from box_sdk_gen.managers.storage_policies import * + +from box_sdk_gen.managers.storage_policy_assignments import * + +from box_sdk_gen.managers.zip_downloads import * + +from box_sdk_gen.managers.sign_requests import * + +from box_sdk_gen.managers.workflows import * + +from box_sdk_gen.managers.sign_templates import * + +from box_sdk_gen.managers.integration_mappings import * + +from box_sdk_gen.managers.ai import * + +from box_sdk_gen.managers.ai_studio import * + +from box_sdk_gen.managers.docgen_template import * + +from box_sdk_gen.managers.docgen import * + +from box_sdk_gen.managers.hubs import * + +from box_sdk_gen.managers.hub_collaborations import * + +from box_sdk_gen.managers.hub_items import * + +from box_sdk_gen.managers.shield_lists import * + +from box_sdk_gen.managers.archives import * + +from box_sdk_gen.managers.external_users import * diff --git a/box_sdk_gen/managers/ai.py b/box_sdk_gen/managers/ai.py new file mode 100644 index 000000000..9e09529a1 --- /dev/null +++ b/box_sdk_gen/managers/ai.py @@ -0,0 +1,482 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.schemas.ai_item_ask import AiItemAsk + +from box_sdk_gen.schemas.ai_dialogue_history import AiDialogueHistory + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_ask import AiAgentAsk + +from box_sdk_gen.schemas.ai_ask_agent import AiAskAgent + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.ai_agent_text_gen import AiAgentTextGen + +from box_sdk_gen.schemas.ai_text_gen_agent import AiTextGenAgent + +from box_sdk_gen.schemas.ai_agent_extract import AiAgentExtract + +from box_sdk_gen.schemas.ai_agent_extract_structured import AiAgentExtractStructured + +from box_sdk_gen.schemas.ai_item_base import AiItemBase + +from box_sdk_gen.schemas.ai_extract_agent import AiExtractAgent + +from box_sdk_gen.schemas.ai_extract_structured_agent import AiExtractStructuredAgent + +from box_sdk_gen.schemas.ai_response_full import AiResponseFull + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.ai_ask import AiAsk + +from box_sdk_gen.schemas.ai_response import AiResponse + +from box_sdk_gen.schemas.ai_text_gen import AiTextGen + +from box_sdk_gen.schemas.ai_agent import AiAgent + +from box_sdk_gen.schemas.ai_extract import AiExtract + +from box_sdk_gen.schemas.ai_extract_structured_response import ( + AiExtractStructuredResponse, +) + +from box_sdk_gen.schemas.ai_extract_structured import AiExtractStructured + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.serialization.json import sd_to_json + + +class CreateAiAskMode(str, Enum): + MULTIPLE_ITEM_QA = 'multiple_item_qa' + SINGLE_ITEM_QA = 'single_item_qa' + + +class CreateAiTextGenItemsTypeField(str, Enum): + FILE = 'file' + + +class CreateAiTextGenItems(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + id: str, + *, + type: CreateAiTextGenItemsTypeField = CreateAiTextGenItemsTypeField.FILE, + content: Optional[str] = None, + **kwargs + ): + """ + :param id: The ID of the item. + :type id: str + :param type: The type of the item., defaults to CreateAiTextGenItemsTypeField.FILE + :type type: CreateAiTextGenItemsTypeField, optional + :param content: The content to use as context for generating new text or editing existing text., defaults to None + :type content: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.content = content + + +class GetAiAgentDefaultConfigMode(str, Enum): + ASK = 'ask' + TEXT_GEN = 'text_gen' + EXTRACT = 'extract' + EXTRACT_STRUCTURED = 'extract_structured' + + +class CreateAiExtractStructuredMetadataTemplateTypeField(str, Enum): + METADATA_TEMPLATE = 'metadata_template' + + +class CreateAiExtractStructuredMetadataTemplate(BaseObject): + _discriminator = 'type', {'metadata_template'} + + def __init__( + self, + *, + template_key: Optional[str] = None, + type: Optional[CreateAiExtractStructuredMetadataTemplateTypeField] = None, + scope: Optional[str] = None, + **kwargs + ): + """ + :param template_key: The name of the metadata template., defaults to None + :type template_key: Optional[str], optional + :param type: Value is always `metadata_template`., defaults to None + :type type: Optional[CreateAiExtractStructuredMetadataTemplateTypeField], optional + :param scope: The scope of the metadata template that can either be global or + enterprise. + * The **global** scope is used for templates that are + available to any Box enterprise. + * The **enterprise** scope represents templates created within a specific enterprise, + containing the ID of that enterprise., defaults to None + :type scope: Optional[str], optional + """ + super().__init__(**kwargs) + self.template_key = template_key + self.type = type + self.scope = scope + + +class CreateAiExtractStructuredFieldsOptionsField(BaseObject): + def __init__(self, key: str, **kwargs): + """ + :param key: A unique identifier for the field. + :type key: str + """ + super().__init__(**kwargs) + self.key = key + + +class CreateAiExtractStructuredFields(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'display_name': 'displayName', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'displayName': 'display_name', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + key: str, + *, + description: Optional[str] = None, + display_name: Optional[str] = None, + prompt: Optional[str] = None, + type: Optional[str] = None, + options: Optional[List[CreateAiExtractStructuredFieldsOptionsField]] = None, + **kwargs + ): + """ + :param key: A unique identifier for the field. + :type key: str + :param description: A description of the field., defaults to None + :type description: Optional[str], optional + :param display_name: The display name of the field., defaults to None + :type display_name: Optional[str], optional + :param prompt: The context about the key that may include how to find and format it., defaults to None + :type prompt: Optional[str], optional + :param type: The type of the field. It include but is not limited to string, float, date, enum, and multiSelect., defaults to None + :type type: Optional[str], optional + :param options: A list of options for this field. This is most often used in combination with the enum and multiSelect field types., defaults to None + :type options: Optional[List[CreateAiExtractStructuredFieldsOptionsField]], optional + """ + super().__init__(**kwargs) + self.key = key + self.description = description + self.display_name = display_name + self.prompt = prompt + self.type = type + self.options = options + + +class AiManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def create_ai_ask( + self, + mode: CreateAiAskMode, + prompt: str, + items: List[AiItemAsk], + *, + dialogue_history: Optional[List[AiDialogueHistory]] = None, + include_citations: Optional[bool] = None, + ai_agent: Optional[AiAskAgent] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Optional[AiResponseFull]: + """ + Sends an AI request to supported LLMs and returns an answer specifically focused on the user's question given the provided context. + :param mode: Box AI handles text documents with text representations up to 1MB in size, or a maximum of 25 files, + whichever comes first. If the text file size exceeds 1MB, the first 1MB of text representation will be processed. + Box AI handles image documents with a resolution of 1024 x 1024 pixels, with a maximum of 5 images or 5 pages + for multi-page images. If the number of image or image pages exceeds 5, the first 5 images or pages will + be processed. If you set mode parameter to `single_item_qa`, the items array can have one element only. + Currently Box AI does not support multi-modal requests. If both images and text are sent Box AI will only + process the text. + :type mode: CreateAiAskMode + :param prompt: The prompt provided by the client to be answered by the LLM. + The prompt's length is limited to 10000 characters. + :type prompt: str + :param items: The items to be processed by the LLM, often files. + :type items: List[AiItemAsk] + :param dialogue_history: The history of prompts and answers previously passed to the LLM. This provides additional context to the LLM in generating the response., defaults to None + :type dialogue_history: Optional[List[AiDialogueHistory]], optional + :param include_citations: A flag to indicate whether citations should be returned., defaults to None + :type include_citations: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'mode': mode, + 'prompt': prompt, + 'items': items, + 'dialogue_history': dialogue_history, + 'include_citations': include_citations, + 'ai_agent': ai_agent, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/ai/ask']), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + if to_string(response.status) == '204': + return None + return deserialize(response.data, AiResponseFull) + + def create_ai_text_gen( + self, + prompt: str, + items: List[CreateAiTextGenItems], + *, + dialogue_history: Optional[List[AiDialogueHistory]] = None, + ai_agent: Optional[AiTextGenAgent] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiResponse: + """ + Sends an AI request to supported Large Language Models (LLMs) and returns generated text based on the provided prompt. + :param prompt: The prompt provided by the client to be answered by the LLM. The prompt's length is limited to 10000 characters. + :type prompt: str + :param items: The items to be processed by the LLM, often files. + The array can include **exactly one** element. + + **Note**: Box AI handles documents with text representations up to 1MB in size. + If the file size exceeds 1MB, the first 1MB of text representation will be processed. + :type items: List[CreateAiTextGenItems] + :param dialogue_history: The history of prompts and answers previously passed to the LLM. This parameter provides the additional context to the LLM when generating the response., defaults to None + :type dialogue_history: Optional[List[AiDialogueHistory]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'prompt': prompt, + 'items': items, + 'dialogue_history': dialogue_history, + 'ai_agent': ai_agent, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/ai/text_gen'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiResponse) + + def get_ai_agent_default_config( + self, + mode: GetAiAgentDefaultConfigMode, + *, + language: Optional[str] = None, + model: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiAgent: + """ + Get the AI agent default config. + :param mode: The mode to filter the agent config to return. + :type mode: GetAiAgentDefaultConfigMode + :param language: The ISO language code to return the agent config for. + If the language is not supported the default agent config is returned., defaults to None + :type language: Optional[str], optional + :param model: The model to return the default agent config for., defaults to None + :type model: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'mode': to_string(mode), + 'language': to_string(language), + 'model': to_string(model), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/ai_agent_default'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiAgent) + + def create_ai_extract( + self, + prompt: str, + items: List[AiItemBase], + *, + ai_agent: Optional[AiExtractAgent] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiResponse: + """ + Sends an AI request to supported Large Language Models (LLMs) and extracts metadata in form of key-value pairs. + + In this request, both the prompt and the output can be freeform. + + + Metadata template setup before sending the request is not required. + + :param prompt: The prompt provided to a Large Language Model (LLM) in the request. The prompt can be up to 10000 characters long and it can be an XML or a JSON schema. + :type prompt: str + :param items: The items that LLM will process. Currently, you can use files only. + :type items: List[AiItemBase] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'prompt': prompt, 'items': items, 'ai_agent': ai_agent} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/ai/extract'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiResponse) + + def create_ai_extract_structured( + self, + items: List[AiItemBase], + *, + metadata_template: Optional[CreateAiExtractStructuredMetadataTemplate] = None, + fields: Optional[List[CreateAiExtractStructuredFields]] = None, + ai_agent: Optional[AiExtractStructuredAgent] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiExtractStructuredResponse: + """ + Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of key-value pairs. + + For this request, you either need a metadata template or a list of fields you want to extract. + + + Input is **either** a metadata template or a list of fields to ensure the structure. + + + To learn more about creating templates, see [Creating metadata templates in the Admin Console](https://support.box.com/hc/en-us/articles/360044194033-Customizing-Metadata-Templates) + + + or use the [metadata template API](g://metadata/templates/create). + + :param items: The items to be processed by the LLM. Currently you can use files only. + :type items: List[AiItemBase] + :param metadata_template: The metadata template containing the fields to extract. + For your request to work, you must provide either `metadata_template` or `fields`, but not both., defaults to None + :type metadata_template: Optional[CreateAiExtractStructuredMetadataTemplate], optional + :param fields: The fields to be extracted from the provided items. + For your request to work, you must provide either `metadata_template` or `fields`, but not both., defaults to None + :type fields: Optional[List[CreateAiExtractStructuredFields]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'items': items, + 'metadata_template': metadata_template, + 'fields': fields, + 'ai_agent': ai_agent, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/ai/extract_structured', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiExtractStructuredResponse) diff --git a/box_sdk_gen/managers/ai_studio.py b/box_sdk_gen/managers/ai_studio.py new file mode 100644 index 000000000..d43740e2a --- /dev/null +++ b/box_sdk_gen/managers/ai_studio.py @@ -0,0 +1,328 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.schemas.ai_agent_allowed_entity import AiAgentAllowedEntity + +from box_sdk_gen.schemas.ai_studio_agent_ask import AiStudioAgentAsk + +from box_sdk_gen.schemas.ai_studio_agent_text_gen import AiStudioAgentTextGen + +from box_sdk_gen.schemas.ai_studio_agent_extract import AiStudioAgentExtract + +from box_sdk_gen.schemas.ai_multiple_agent_response import AiMultipleAgentResponse + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.ai_single_agent_response_full import AiSingleAgentResponseFull + +from box_sdk_gen.schemas.create_ai_agent import CreateAiAgent + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateAiAgentType(str, Enum): + AI_AGENT = 'ai_agent' + + +class UpdateAiAgentByIdType(str, Enum): + AI_AGENT = 'ai_agent' + + +class AiStudioManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_ai_agents( + self, + *, + mode: Optional[List[str]] = None, + fields: Optional[List[str]] = None, + agent_state: Optional[List[str]] = None, + include_box_default: Optional[bool] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiMultipleAgentResponse: + """ + Lists AI agents based on the provided parameters. + :param mode: The mode to filter the agent config to return. Possible values are: `ask`, `text_gen`, and `extract`., defaults to None + :type mode: Optional[List[str]], optional + :param fields: The fields to return in the response., defaults to None + :type fields: Optional[List[str]], optional + :param agent_state: The state of the agents to return. Possible values are: `enabled`, `disabled` and `enabled_for_selected_users`., defaults to None + :type agent_state: Optional[List[str]], optional + :param include_box_default: Whether to include the Box default agents in the response., defaults to None + :type include_box_default: Optional[bool], optional + :param marker: Defines the position marker at which to begin returning results., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'mode': to_string(mode), + 'fields': to_string(fields), + 'agent_state': to_string(agent_state), + 'include_box_default': to_string(include_box_default), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/ai_agents'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiMultipleAgentResponse) + + def create_ai_agent( + self, + name: str, + access_state: str, + *, + type: CreateAiAgentType = CreateAiAgentType.AI_AGENT, + icon_reference: Optional[str] = None, + allowed_entities: Optional[List[AiAgentAllowedEntity]] = None, + ask: Optional[AiStudioAgentAsk] = None, + text_gen: Optional[AiStudioAgentTextGen] = None, + extract: Optional[AiStudioAgentExtract] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiSingleAgentResponseFull: + """ + Creates an AI agent. At least one of the following capabilities must be provided: `ask`, `text_gen`, `extract`. + :param name: The name of the AI Agent. + :type name: str + :param access_state: The state of the AI Agent. Possible values are: `enabled`, `disabled`, and `enabled_for_selected_users`. + :type access_state: str + :param type: The type of agent used to handle queries., defaults to CreateAiAgentType.AI_AGENT + :type type: CreateAiAgentType, optional + :param icon_reference: The icon reference of the AI Agent. It should have format of the URL `https://cdn01.boxcdn.net/app-assets/aistudio/avatars/` + where possible values of `file_name` are: `logo_boxAi.png`,`logo_stamp.png`,`logo_legal.png`,`logo_finance.png`,`logo_config.png`,`logo_handshake.png`,`logo_analytics.png`,`logo_classification.png`., defaults to None + :type icon_reference: Optional[str], optional + :param allowed_entities: List of allowed users or groups., defaults to None + :type allowed_entities: Optional[List[AiAgentAllowedEntity]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'type': type, + 'name': name, + 'access_state': access_state, + 'icon_reference': icon_reference, + 'allowed_entities': allowed_entities, + 'ask': ask, + 'text_gen': text_gen, + 'extract': extract, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/ai_agents'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiSingleAgentResponseFull) + + def update_ai_agent_by_id( + self, + agent_id: str, + name: str, + access_state: str, + *, + type: UpdateAiAgentByIdType = UpdateAiAgentByIdType.AI_AGENT, + icon_reference: Optional[str] = None, + allowed_entities: Optional[List[AiAgentAllowedEntity]] = None, + ask: Optional[AiStudioAgentAsk] = None, + text_gen: Optional[AiStudioAgentTextGen] = None, + extract: Optional[AiStudioAgentExtract] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiSingleAgentResponseFull: + """ + Updates an AI agent. + :param agent_id: The ID of the agent to update. + Example: "1234" + :type agent_id: str + :param name: The name of the AI Agent. + :type name: str + :param access_state: The state of the AI Agent. Possible values are: `enabled`, `disabled`, and `enabled_for_selected_users`. + :type access_state: str + :param type: The type of agent used to handle queries., defaults to UpdateAiAgentByIdType.AI_AGENT + :type type: UpdateAiAgentByIdType, optional + :param icon_reference: The icon reference of the AI Agent. It should have format of the URL `https://cdn01.boxcdn.net/app-assets/aistudio/avatars/` + where possible values of `file_name` are: `logo_boxAi.png`,`logo_stamp.png`,`logo_legal.png`,`logo_finance.png`,`logo_config.png`,`logo_handshake.png`,`logo_analytics.png`,`logo_classification.png`., defaults to None + :type icon_reference: Optional[str], optional + :param allowed_entities: List of allowed users or groups., defaults to None + :type allowed_entities: Optional[List[AiAgentAllowedEntity]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'type': type, + 'name': name, + 'access_state': access_state, + 'icon_reference': icon_reference, + 'allowed_entities': allowed_entities, + 'ask': ask, + 'text_gen': text_gen, + 'extract': extract, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/ai_agents/', + to_string(agent_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiSingleAgentResponseFull) + + def get_ai_agent_by_id( + self, + agent_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AiSingleAgentResponseFull: + """ + Gets an AI Agent using the `agent_id` parameter. + :param agent_id: The agent id to get. + Example: "1234" + :type agent_id: str + :param fields: The fields to return in the response., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/ai_agents/', + to_string(agent_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AiSingleAgentResponseFull) + + def delete_ai_agent_by_id( + self, agent_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes an AI agent using the provided parameters. + :param agent_id: The ID of the agent to delete. + Example: "1234" + :type agent_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/ai_agents/', + to_string(agent_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/app_item_associations.py b/box_sdk_gen/managers/app_item_associations.py new file mode 100644 index 000000000..6f32278ed --- /dev/null +++ b/box_sdk_gen/managers/app_item_associations.py @@ -0,0 +1,197 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.app_item_associations import AppItemAssociations + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class AppItemAssociationsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_app_item_associations( + self, + file_id: str, + *, + limit: Optional[int] = None, + marker: Optional[str] = None, + application_type: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AppItemAssociations: + """ + **This is a beta feature, which means that its availability might be limited.** + + Returns all app items the file is associated with. This includes app items + + + associated with ancestors of the file. Assuming the context user has access + + + to the file, the type/ids are revealed even if the context user does not + + + have **View** permission on the app item. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param application_type: If given, only return app items for this application type., defaults to None + :type application_type: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'limit': to_string(limit), + 'marker': to_string(marker), + 'application_type': to_string(application_type), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/app_item_associations', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AppItemAssociations) + + def get_folder_app_item_associations( + self, + folder_id: str, + *, + limit: Optional[int] = None, + marker: Optional[str] = None, + application_type: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AppItemAssociations: + """ + **This is a beta feature, which means that its availability might be limited.** + + Returns all app items the folder is associated with. This includes app items + + + associated with ancestors of the folder. Assuming the context user has access + + + to the folder, the type/ids are revealed even if the context user does not + + + have **View** permission on the app item. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param application_type: If given, returns only app items for this application type., defaults to None + :type application_type: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'limit': to_string(limit), + 'marker': to_string(marker), + 'application_type': to_string(application_type), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/app_item_associations', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AppItemAssociations) diff --git a/box_sdk_gen/managers/archives.py b/box_sdk_gen/managers/archives.py new file mode 100644 index 000000000..6cfcf5258 --- /dev/null +++ b/box_sdk_gen/managers/archives.py @@ -0,0 +1,180 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.archives_v2025_r0 import ArchivesV2025R0 + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.archive_v2025_r0 import ArchiveV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class ArchivesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_archives_v2025_r0( + self, + *, + limit: Optional[int] = None, + marker: Optional[str] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ArchivesV2025R0: + """ + Retrieves archives for an enterprise. + + To learn more about the archive APIs, see the [Archive API Guide](g://archives). + + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination., defaults to None + :type marker: Optional[str], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'limit': to_string(limit), 'marker': to_string(marker)} + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/archives']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ArchivesV2025R0) + + def create_archive_v2025_r0( + self, + name: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ArchiveV2025R0: + """ + Creates an archive. + + To learn more about the archive APIs, see the [Archive API Guide](g://archives). + + :param name: The name of the archive. + :type name: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/archives']), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ArchiveV2025R0) + + def delete_archive_by_id_v2025_r0( + self, + archive_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Permanently deletes an archive. + + To learn more about the archive APIs, see the [Archive API Guide](g://archives). + + :param archive_id: The ID of the archive. + Example: "982312" + :type archive_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/archives/', + to_string(archive_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/authorization.py b/box_sdk_gen/managers/authorization.py new file mode 100644 index 000000000..7bcb2af9c --- /dev/null +++ b/box_sdk_gen/managers/authorization.py @@ -0,0 +1,434 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenGrantTypeField + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenSubjectTokenTypeField + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2TokenBoxSubjectTypeField + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.access_token import AccessToken + +from box_sdk_gen.schemas.o_auth_2_error import OAuth2Error + +from box_sdk_gen.schemas.post_o_auth_2_token import PostOAuth2Token + +from box_sdk_gen.schemas.post_o_auth_2_token_refresh_access_token import ( + PostOAuth2TokenRefreshAccessToken, +) + +from box_sdk_gen.schemas.post_o_auth_2_revoke import PostOAuth2Revoke + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class AuthorizeUserResponseType(str, Enum): + CODE = 'code' + + +class RequestAccessTokenGrantType(str, Enum): + AUTHORIZATION_CODE = 'authorization_code' + REFRESH_TOKEN = 'refresh_token' + CLIENT_CREDENTIALS = 'client_credentials' + URN_IETF_PARAMS_OAUTH_GRANT_TYPE_JWT_BEARER = ( + 'urn:ietf:params:oauth:grant-type:jwt-bearer' + ) + URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE = ( + 'urn:ietf:params:oauth:grant-type:token-exchange' + ) + + +class RequestAccessTokenSubjectTokenType(str, Enum): + URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN = ( + 'urn:ietf:params:oauth:token-type:access_token' + ) + + +class RequestAccessTokenActorTokenType(str, Enum): + URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ID_TOKEN = ( + 'urn:ietf:params:oauth:token-type:id_token' + ) + + +class RequestAccessTokenBoxSubjectType(str, Enum): + ENTERPRISE = 'enterprise' + USER = 'user' + + +class RefreshAccessTokenGrantType(str, Enum): + REFRESH_TOKEN = 'refresh_token' + + +class AuthorizationManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def authorize_user( + self, + response_type: AuthorizeUserResponseType, + client_id: str, + *, + redirect_uri: Optional[str] = None, + state: Optional[str] = None, + scope: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Authorize a user by sending them through the [Box](https://box.com) + + website and request their permission to act on their behalf. + + + This is the first step when authenticating a user using + + + OAuth 2.0. To request a user's authorization to use the Box APIs + + + on their behalf you will need to send a user to the URL with this + + + format. + + :param response_type: The type of response we'd like to receive. + :type response_type: AuthorizeUserResponseType + :param client_id: The Client ID of the application that is requesting to authenticate + the user. To get the Client ID for your application, log in to your + Box developer console and click the **Edit Application** link for + the application you're working with. In the OAuth 2.0 Parameters section + of the configuration page, find the item labelled `client_id`. The + text of that item is your application's Client ID. + :type client_id: str + :param redirect_uri: The URI to which Box redirects the browser after the user has granted + or denied the application permission. This URI match one of the redirect + URIs in the configuration of your application. It must be a + valid HTTPS URI and it needs to be able to handle the redirection to + complete the next step in the OAuth 2.0 flow. + Although this parameter is optional, it must be a part of the + authorization URL if you configured multiple redirect URIs + for the application in the developer console. A missing parameter causes + a `redirect_uri_missing` error after the user grants application access., defaults to None + :type redirect_uri: Optional[str], optional + :param state: A custom string of your choice. Box will pass the same string to + the redirect URL when authentication is complete. This parameter + can be used to identify a user on redirect, as well as protect + against hijacked sessions and other exploits., defaults to None + :type state: Optional[str], optional + :param scope: A space-separated list of application scopes you'd like to + authenticate the user for. This defaults to all the scopes configured + for the application in its configuration page., defaults to None + :type scope: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'response_type': to_string(response_type), + 'client_id': to_string(client_id), + 'redirect_uri': to_string(redirect_uri), + 'state': to_string(state), + 'scope': to_string(scope), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.oauth_2_url, '/authorize']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def request_access_token( + self, + grant_type: RequestAccessTokenGrantType, + *, + client_id: Optional[str] = None, + client_secret: Optional[str] = None, + code: Optional[str] = None, + refresh_token: Optional[str] = None, + assertion: Optional[str] = None, + subject_token: Optional[str] = None, + subject_token_type: Optional[RequestAccessTokenSubjectTokenType] = None, + actor_token: Optional[str] = None, + actor_token_type: Optional[RequestAccessTokenActorTokenType] = None, + scope: Optional[str] = None, + resource: Optional[str] = None, + box_subject_type: Optional[RequestAccessTokenBoxSubjectType] = None, + box_subject_id: Optional[str] = None, + box_shared_link: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AccessToken: + """ + Request an Access Token using either a client-side obtained OAuth 2.0 + + authorization code or a server-side JWT assertion. + + + An Access Token is a string that enables Box to verify that a + + + request belongs to an authorized session. In the normal order of + + + operations you will begin by requesting authentication from the + + + [authorize](#get-authorize) endpoint and Box will send you an + + + authorization code. + + + You will then send this code to this endpoint to exchange it for + + + an Access Token. The returned Access Token can then be used to to make + + + Box API calls. + + :param grant_type: The type of request being made, either using a client-side obtained + authorization code, a refresh token, a JWT assertion, client credentials + grant or another access token for the purpose of downscoping a token. + :type grant_type: RequestAccessTokenGrantType + :param client_id: The Client ID of the application requesting an access token. + + Used in combination with `authorization_code`, `client_credentials`, or + `urn:ietf:params:oauth:grant-type:jwt-bearer` as the `grant_type`., defaults to None + :type client_id: Optional[str], optional + :param client_secret: The client secret of the application requesting an access token. + + Used in combination with `authorization_code`, `client_credentials`, or + `urn:ietf:params:oauth:grant-type:jwt-bearer` as the `grant_type`., defaults to None + :type client_secret: Optional[str], optional + :param code: The client-side authorization code passed to your application by + Box in the browser redirect after the user has successfully + granted your application permission to make API calls on their + behalf. + + Used in combination with `authorization_code` as the `grant_type`., defaults to None + :type code: Optional[str], optional + :param refresh_token: A refresh token used to get a new access token with. + + Used in combination with `refresh_token` as the `grant_type`., defaults to None + :type refresh_token: Optional[str], optional + :param assertion: A JWT assertion for which to request a new access token. + + Used in combination with `urn:ietf:params:oauth:grant-type:jwt-bearer` + as the `grant_type`., defaults to None + :type assertion: Optional[str], optional + :param subject_token: The token to exchange for a downscoped token. This can be a regular + access token, a JWT assertion, or an app token. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type subject_token: Optional[str], optional + :param subject_token_type: The type of `subject_token` passed in. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type subject_token_type: Optional[RequestAccessTokenSubjectTokenType], optional + :param actor_token: The token used to create an annotator token. + This is a JWT assertion. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type actor_token: Optional[str], optional + :param actor_token_type: The type of `actor_token` passed in. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type actor_token_type: Optional[RequestAccessTokenActorTokenType], optional + :param scope: The space-delimited list of scopes that you want apply to the + new access token. + + The `subject_token` will need to have all of these scopes or + the call will error with **401 Unauthorized**.., defaults to None + :type scope: Optional[str], optional + :param resource: Full URL for the file that the token should be generated for., defaults to None + :type resource: Optional[str], optional + :param box_subject_type: Used in combination with `client_credentials` as the `grant_type`., defaults to None + :type box_subject_type: Optional[RequestAccessTokenBoxSubjectType], optional + :param box_subject_id: Used in combination with `client_credentials` as the `grant_type`. + Value is determined by `box_subject_type`. If `user` use user ID and if + `enterprise` use enterprise ID., defaults to None + :type box_subject_id: Optional[str], optional + :param box_shared_link: Full URL of the shared link on the file or folder + that the token should be generated for., defaults to None + :type box_shared_link: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'grant_type': grant_type, + 'client_id': client_id, + 'client_secret': client_secret, + 'code': code, + 'refresh_token': refresh_token, + 'assertion': assertion, + 'subject_token': subject_token, + 'subject_token_type': subject_token_type, + 'actor_token': actor_token, + 'actor_token_type': actor_token_type, + 'scope': scope, + 'resource': resource, + 'box_subject_type': box_subject_type, + 'box_subject_id': box_subject_id, + 'box_shared_link': box_shared_link, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/oauth2/token']), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/x-www-form-urlencoded', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AccessToken) + + def refresh_access_token( + self, + client_id: str, + client_secret: str, + refresh_token: str, + *, + grant_type: RefreshAccessTokenGrantType = RefreshAccessTokenGrantType.REFRESH_TOKEN, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AccessToken: + """ + Refresh an Access Token using its client ID, secret, and refresh token. + :param client_id: The client ID of the application requesting to refresh the token. + :type client_id: str + :param client_secret: The client secret of the application requesting to refresh the token. + :type client_secret: str + :param refresh_token: The refresh token to refresh. + :type refresh_token: str + :param grant_type: The type of request being made, in this case a refresh request., defaults to RefreshAccessTokenGrantType.REFRESH_TOKEN + :type grant_type: RefreshAccessTokenGrantType, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'grant_type': grant_type, + 'client_id': client_id, + 'client_secret': client_secret, + 'refresh_token': refresh_token, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/oauth2/token#refresh'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/x-www-form-urlencoded', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AccessToken) + + def revoke_access_token( + self, + *, + client_id: Optional[str] = None, + client_secret: Optional[str] = None, + token: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Revoke an active Access Token, effectively logging a user out + + that has been previously authenticated. + + :param client_id: The Client ID of the application requesting to revoke the + access token., defaults to None + :type client_id: Optional[str], optional + :param client_secret: The client secret of the application requesting to revoke + an access token., defaults to None + :type client_secret: Optional[str], optional + :param token: The access token to revoke., defaults to None + :type token: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'client_id': client_id, + 'client_secret': client_secret, + 'token': token, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/oauth2/revoke'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/x-www-form-urlencoded', + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/avatars.py b/box_sdk_gen/managers/avatars.py new file mode 100644 index 000000000..a6ff17103 --- /dev/null +++ b/box_sdk_gen/managers/avatars.py @@ -0,0 +1,174 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.user_avatar import UserAvatar + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.networking.fetch_options import MultipartItem + +from box_sdk_gen.serialization.json import SerializedData + + +class AvatarsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_user_avatar( + self, user_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ByteStream: + """ + Retrieves an image of a the user's avatar. + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/avatar', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.BINARY, + auth=self.auth, + network_session=self.network_session, + ) + ) + return response.content + + def create_user_avatar( + self, + user_id: str, + pic: ByteStream, + *, + pic_file_name: Optional[str] = None, + pic_content_type: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UserAvatar: + """ + Adds or updates a user avatar. + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param pic: The image file to be uploaded to Box. + Accepted file extensions are `.jpg` or `.png`. + The maximum file size is 1MB. + :type pic: ByteStream + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'pic': pic, + 'pic_file_name': pic_file_name, + 'pic_content_type': pic_content_type, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/avatar', + ] + ), + method='POST', + headers=headers_map, + multipart_data=[ + MultipartItem( + part_name='pic', + file_stream=pic, + file_name=pic_file_name, + content_type=pic_content_type, + ) + ], + content_type='multipart/form-data', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UserAvatar) + + def delete_user_avatar( + self, user_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes an existing user avatar. + + You cannot reverse this operation. + + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/avatar', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/chunked_uploads.py b/box_sdk_gen/managers/chunked_uploads.py new file mode 100644 index 000000000..1ff148681 --- /dev/null +++ b/box_sdk_gen/managers/chunked_uploads.py @@ -0,0 +1,872 @@ +from typing import List + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.internal.utils import Buffer + +from box_sdk_gen.internal.utils import HashName + +from box_sdk_gen.internal.utils import Iterator + +from box_sdk_gen.schemas.upload_session import UploadSession + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.uploaded_part import UploadedPart + +from box_sdk_gen.schemas.upload_parts import UploadParts + +from box_sdk_gen.schemas.files import Files + +from box_sdk_gen.schemas.upload_part import UploadPart + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.internal.utils import generate_byte_stream_from_buffer + +from box_sdk_gen.internal.utils import hex_to_base_64 + +from box_sdk_gen.internal.utils import iterate_chunks + +from box_sdk_gen.internal.utils import read_byte_stream + +from box_sdk_gen.internal.utils import reduce_iterator + +from box_sdk_gen.internal.utils import Hash + +from box_sdk_gen.internal.utils import buffer_length + +from box_sdk_gen.schemas.file_full import FileFull + + +class _PartAccumulator: + def __init__( + self, + last_index: int, + parts: List[UploadPart], + file_size: int, + upload_part_url: str, + file_hash: Hash, + ): + self.last_index = last_index + self.parts = parts + self.file_size = file_size + self.upload_part_url = upload_part_url + self.file_hash = file_hash + + +class ChunkedUploadsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def create_file_upload_session( + self, + folder_id: str, + file_size: int, + file_name: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadSession: + """ + Creates an upload session for a new file. + :param folder_id: The ID of the folder to upload the new file to. + :type folder_id: str + :param file_size: The total number of bytes of the file to be uploaded. + :type file_size: int + :param file_name: The name of new file. + :type file_name: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'folder_id': folder_id, + 'file_size': file_size, + 'file_name': file_name, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/upload_sessions', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadSession) + + def create_file_upload_session_for_existing_file( + self, + file_id: str, + file_size: int, + *, + file_name: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadSession: + """ + Creates an upload session for an existing file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param file_size: The total number of bytes of the file to be uploaded. + :type file_size: int + :param file_name: The optional new name of new file., defaults to None + :type file_name: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'file_size': file_size, 'file_name': file_name} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/', + to_string(file_id), + '/upload_sessions', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadSession) + + def get_file_upload_session_by_url( + self, url: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadSession: + """ + Using this method with urls provided in response when creating a new upload session is preferred to use over GetFileUploadSessionById method. + + This allows to always upload your content to the closest Box data center and can significantly improve upload speed. + + + Return information about an upload session. + + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) endpoint. + + :param url: URL of getFileUploadSessionById method + :type url: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=url, + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadSession) + + def get_file_upload_session_by_id( + self, + upload_session_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadSession: + """ + Return information about an upload session. + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) endpoint. + + :param upload_session_id: The ID of the upload session. + Example: "D5E3F7A" + :type upload_session_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/upload_sessions/', + to_string(upload_session_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadSession) + + def upload_file_part_by_url( + self, + url: str, + request_body: ByteStream, + digest: str, + content_range: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadedPart: + """ + Using this method with urls provided in response when creating a new upload session is preferred to use over UploadFilePart method. + + This allows to always upload your content to the closest Box data center and can significantly improve upload speed. + + + Uploads a chunk of a file for an upload session. + + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param url: URL of uploadFilePart method + :type url: str + :param request_body: Request body of uploadFilePart method + :type request_body: ByteStream + :param digest: The [RFC3230][1] message digest of the chunk uploaded. + + Only SHA1 is supported. The SHA1 digest must be base64 + encoded. The format of this header is as + `sha=BASE64_ENCODED_DIGEST`. + + To get the value for the `SHA` digest, use the + openSSL command to encode the file part: + `openssl sha1 -binary | base64`. + + [1]: https://tools.ietf.org/html/rfc3230 + :type digest: str + :param content_range: The byte range of the chunk. + + Must not overlap with the range of a part already + uploaded this session. Each part’s size must be + exactly equal in size to the part size specified + in the upload session that you created. + One exception is the last part of the file, as this can be smaller. + + When providing the value for `content-range`, remember that: + + * The lower bound of each part's byte range + must be a multiple of the part size. + * The higher bound must be a multiple of the part size - 1. + :type content_range: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + { + 'digest': to_string(digest), + 'content-range': to_string(content_range), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=url, + method='PUT', + headers=headers_map, + file_stream=request_body, + content_type='application/octet-stream', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadedPart) + + def upload_file_part( + self, + upload_session_id: str, + request_body: ByteStream, + digest: str, + content_range: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadedPart: + """ + Uploads a chunk of a file for an upload session. + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param upload_session_id: The ID of the upload session. + Example: "D5E3F7A" + :type upload_session_id: str + :param request_body: Request body of uploadFilePart method + :type request_body: ByteStream + :param digest: The [RFC3230][1] message digest of the chunk uploaded. + + Only SHA1 is supported. The SHA1 digest must be base64 + encoded. The format of this header is as + `sha=BASE64_ENCODED_DIGEST`. + + To get the value for the `SHA` digest, use the + openSSL command to encode the file part: + `openssl sha1 -binary | base64`. + + [1]: https://tools.ietf.org/html/rfc3230 + :type digest: str + :param content_range: The byte range of the chunk. + + Must not overlap with the range of a part already + uploaded this session. Each part’s size must be + exactly equal in size to the part size specified + in the upload session that you created. + One exception is the last part of the file, as this can be smaller. + + When providing the value for `content-range`, remember that: + + * The lower bound of each part's byte range + must be a multiple of the part size. + * The higher bound must be a multiple of the part size - 1. + :type content_range: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + { + 'digest': to_string(digest), + 'content-range': to_string(content_range), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/upload_sessions/', + to_string(upload_session_id), + ] + ), + method='PUT', + headers=headers_map, + file_stream=request_body, + content_type='application/octet-stream', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadedPart) + + def delete_file_upload_session_by_url( + self, url: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Using this method with urls provided in response when creating a new upload session is preferred to use over DeleteFileUploadSessionById method. + + This allows to always upload your content to the closest Box data center and can significantly improve upload speed. + + + Abort an upload session and discard all data uploaded. + + + This cannot be reversed. + + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param url: URL of deleteFileUploadSessionById method + :type url: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=url, + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def delete_file_upload_session_by_id( + self, + upload_session_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Abort an upload session and discard all data uploaded. + + This cannot be reversed. + + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param upload_session_id: The ID of the upload session. + Example: "D5E3F7A" + :type upload_session_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/upload_sessions/', + to_string(upload_session_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_file_upload_session_parts_by_url( + self, + url: str, + *, + offset: Optional[int] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadParts: + """ + Using this method with urls provided in response when creating a new upload session is preferred to use over GetFileUploadSessionParts method. + + This allows to always upload your content to the closest Box data center and can significantly improve upload speed. + + + Return a list of the chunks uploaded to the upload session so far. + + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param url: URL of getFileUploadSessionParts method + :type url: str + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'offset': to_string(offset), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=url, + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadParts) + + def get_file_upload_session_parts( + self, + upload_session_id: str, + *, + offset: Optional[int] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadParts: + """ + Return a list of the chunks uploaded to the upload session so far. + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param upload_session_id: The ID of the upload session. + Example: "D5E3F7A" + :type upload_session_id: str + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'offset': to_string(offset), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/upload_sessions/', + to_string(upload_session_id), + '/parts', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadParts) + + def create_file_upload_session_commit_by_url( + self, + url: str, + parts: List[UploadPart], + digest: str, + *, + if_match: Optional[str] = None, + if_none_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Optional[Files]: + """ + Using this method with urls provided in response when creating a new upload session is preferred to use over CreateFileUploadSessionCommit method. + + This allows to always upload your content to the closest Box data center and can significantly improve upload speed. + + + Close an upload session and create a file from the uploaded chunks. + + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param url: URL of createFileUploadSessionCommit method + :type url: str + :param parts: The list details for the uploaded parts. + :type parts: List[UploadPart] + :param digest: The [RFC3230][1] message digest of the whole file. + + Only SHA1 is supported. The SHA1 digest must be Base64 + encoded. The format of this header is as + `sha=BASE64_ENCODED_DIGEST`. + + [1]: https://tools.ietf.org/html/rfc3230 + :type digest: str + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param if_none_match: Ensures an item is only returned if it has changed. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `304 Not Modified` if the item has not + changed since., defaults to None + :type if_none_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'parts': parts} + headers_map: Dict[str, str] = prepare_params( + { + 'digest': to_string(digest), + 'if-match': to_string(if_match), + 'if-none-match': to_string(if_none_match), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=url, + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + if to_string(response.status) == '202': + return None + return deserialize(response.data, Files) + + def create_file_upload_session_commit( + self, + upload_session_id: str, + parts: List[UploadPart], + digest: str, + *, + if_match: Optional[str] = None, + if_none_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Optional[Files]: + """ + Close an upload session and create a file from the uploaded chunks. + + The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions) + + + and [`Get upload session`](e://get-files-upload-sessions-id) endpoints. + + :param upload_session_id: The ID of the upload session. + Example: "D5E3F7A" + :type upload_session_id: str + :param parts: The list details for the uploaded parts. + :type parts: List[UploadPart] + :param digest: The [RFC3230][1] message digest of the whole file. + + Only SHA1 is supported. The SHA1 digest must be Base64 + encoded. The format of this header is as + `sha=BASE64_ENCODED_DIGEST`. + + [1]: https://tools.ietf.org/html/rfc3230 + :type digest: str + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param if_none_match: Ensures an item is only returned if it has changed. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `304 Not Modified` if the item has not + changed since., defaults to None + :type if_none_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'parts': parts} + headers_map: Dict[str, str] = prepare_params( + { + 'digest': to_string(digest), + 'if-match': to_string(if_match), + 'if-none-match': to_string(if_none_match), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/upload_sessions/', + to_string(upload_session_id), + '/commit', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + if to_string(response.status) == '202': + return None + return deserialize(response.data, Files) + + def _reducer(self, acc: _PartAccumulator, chunk: ByteStream) -> _PartAccumulator: + last_index: int = acc.last_index + parts: List[UploadPart] = acc.parts + chunk_buffer: Buffer = read_byte_stream(chunk) + hash: Hash = Hash(algorithm=HashName.SHA1) + hash.update_hash(chunk_buffer) + sha_1: str = hash.digest_hash('base64') + digest: str = ''.join(['sha=', sha_1]) + chunk_size: int = buffer_length(chunk_buffer) + bytes_start: int = last_index + 1 + bytes_end: int = last_index + chunk_size + content_range: str = ''.join( + [ + 'bytes ', + to_string(bytes_start), + '-', + to_string(bytes_end), + '/', + to_string(acc.file_size), + ] + ) + uploaded_part: UploadedPart = self.upload_file_part_by_url( + acc.upload_part_url, + generate_byte_stream_from_buffer(chunk_buffer), + digest, + content_range, + ) + part: UploadPart = uploaded_part.part + part_sha_1: str = hex_to_base_64(part.sha_1) + assert part_sha_1 == sha_1 + assert part.size == chunk_size + assert part.offset == bytes_start + acc.file_hash.update_hash(chunk_buffer) + return _PartAccumulator( + last_index=bytes_end, + parts=parts + [part], + file_size=acc.file_size, + upload_part_url=acc.upload_part_url, + file_hash=acc.file_hash, + ) + + def upload_big_file( + self, file: ByteStream, file_name: str, file_size: int, parent_folder_id: str + ) -> FileFull: + """ + Starts the process of chunk uploading a big file. Should return a File object representing uploaded file. + :param file: The stream of the file to upload. + :type file: ByteStream + :param file_name: The name of the file, which will be used for storage in Box. + :type file_name: str + :param file_size: The total size of the file for the chunked upload in bytes. + :type file_size: int + :param parent_folder_id: The ID of the folder where the file should be uploaded. + :type parent_folder_id: str + """ + upload_session: UploadSession = self.create_file_upload_session( + parent_folder_id, file_size, file_name + ) + upload_part_url: str = upload_session.session_endpoints.upload_part + commit_url: str = upload_session.session_endpoints.commit + list_parts_url: str = upload_session.session_endpoints.list_parts + part_size: int = upload_session.part_size + total_parts: int = upload_session.total_parts + assert part_size * total_parts >= file_size + assert upload_session.num_parts_processed == 0 + file_hash: Hash = Hash(algorithm=HashName.SHA1) + chunks_iterator: Iterator = iterate_chunks(file, part_size, file_size) + results: _PartAccumulator = reduce_iterator( + chunks_iterator, + self._reducer, + _PartAccumulator( + last_index=-1, + parts=[], + file_size=file_size, + upload_part_url=upload_part_url, + file_hash=file_hash, + ), + ) + parts: List[UploadPart] = results.parts + processed_session_parts: UploadParts = ( + self.get_file_upload_session_parts_by_url(list_parts_url) + ) + assert processed_session_parts.total_count == total_parts + sha_1: str = file_hash.digest_hash('base64') + digest: str = ''.join(['sha=', sha_1]) + committed_session: Optional[Files] = ( + self.create_file_upload_session_commit_by_url(commit_url, parts, digest) + ) + return committed_session.entries[0] diff --git a/box_sdk_gen/managers/classifications.py b/box_sdk_gen/managers/classifications.py new file mode 100644 index 000000000..a76312ce7 --- /dev/null +++ b/box_sdk_gen/managers/classifications.py @@ -0,0 +1,707 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.classification_template import ClassificationTemplate + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + + +class AddClassificationRequestBodyOpField(str, Enum): + ADDENUMOPTION = 'addEnumOption' + + +class AddClassificationRequestBodyFieldKeyField(str, Enum): + BOX__SECURITY__CLASSIFICATION__KEY = 'Box__Security__Classification__Key' + + +class AddClassificationRequestBodyDataStaticConfigClassificationField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'classification_definition': 'classificationDefinition', + 'color_id': 'colorID', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'classificationDefinition': 'classification_definition', + 'colorID': 'color_id', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + classification_definition: Optional[str] = None, + color_id: Optional[int] = None, + **kwargs + ): + """ + :param classification_definition: A longer description of the classification., defaults to None + :type classification_definition: Optional[str], optional + :param color_id: An internal Box identifier used to assign a color to + a classification label. + + Mapping between a `colorID` and a color may change + without notice. Currently, the color mappings are as + follows. + + * `0`: Yellow. + * `1`: Orange. + * `2`: Watermelon red. + * `3`: Purple rain. + * `4`: Light blue. + * `5`: Dark blue. + * `6`: Light green. + * `7`: Gray., defaults to None + :type color_id: Optional[int], optional + """ + super().__init__(**kwargs) + self.classification_definition = classification_definition + self.color_id = color_id + + +class AddClassificationRequestBodyDataStaticConfigField(BaseObject): + def __init__( + self, + *, + classification: Optional[ + AddClassificationRequestBodyDataStaticConfigClassificationField + ] = None, + **kwargs + ): + """ + :param classification: Additional details for the classification., defaults to None + :type classification: Optional[AddClassificationRequestBodyDataStaticConfigClassificationField], optional + """ + super().__init__(**kwargs) + self.classification = classification + + +class AddClassificationRequestBodyDataField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'static_config': 'staticConfig', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'staticConfig': 'static_config', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + key: str, + *, + static_config: Optional[ + AddClassificationRequestBodyDataStaticConfigField + ] = None, + **kwargs + ): + """ + :param key: The label of the classification as shown in the web and + mobile interfaces. This is the only field required to + add a classification. + :type key: str + :param static_config: A static configuration for the classification., defaults to None + :type static_config: Optional[AddClassificationRequestBodyDataStaticConfigField], optional + """ + super().__init__(**kwargs) + self.key = key + self.static_config = static_config + + +class AddClassificationRequestBody(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'field_key': 'fieldKey', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'fieldKey': 'field_key', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + data: AddClassificationRequestBodyDataField, + *, + op: AddClassificationRequestBodyOpField = AddClassificationRequestBodyOpField.ADDENUMOPTION, + field_key: AddClassificationRequestBodyFieldKeyField = AddClassificationRequestBodyFieldKeyField.BOX__SECURITY__CLASSIFICATION__KEY, + **kwargs + ): + """ + :param data: The details of the classification to add. + :type data: AddClassificationRequestBodyDataField + :param op: The type of change to perform on the classification + object., defaults to AddClassificationRequestBodyOpField.ADDENUMOPTION + :type op: AddClassificationRequestBodyOpField, optional + :param field_key: Defines classifications + available in the enterprise., defaults to AddClassificationRequestBodyFieldKeyField.BOX__SECURITY__CLASSIFICATION__KEY + :type field_key: AddClassificationRequestBodyFieldKeyField, optional + """ + super().__init__(**kwargs) + self.data = data + self.op = op + self.field_key = field_key + + +class UpdateClassificationRequestBodyOpField(str, Enum): + EDITENUMOPTION = 'editEnumOption' + + +class UpdateClassificationRequestBodyFieldKeyField(str, Enum): + BOX__SECURITY__CLASSIFICATION__KEY = 'Box__Security__Classification__Key' + + +class UpdateClassificationRequestBodyDataStaticConfigClassificationField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'classification_definition': 'classificationDefinition', + 'color_id': 'colorID', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'classificationDefinition': 'classification_definition', + 'colorID': 'color_id', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + classification_definition: Optional[str] = None, + color_id: Optional[int] = None, + **kwargs + ): + """ + :param classification_definition: A longer description of the classification., defaults to None + :type classification_definition: Optional[str], optional + :param color_id: An internal Box identifier used to assign a color to + a classification label. + + Mapping between a `colorID` and a color may change + without notice. Currently, the color mappings are as + follows. + + * `0`: Yellow. + * `1`: Orange. + * `2`: Watermelon red. + * `3`: Purple rain. + * `4`: Light blue. + * `5`: Dark blue. + * `6`: Light green. + * `7`: Gray., defaults to None + :type color_id: Optional[int], optional + """ + super().__init__(**kwargs) + self.classification_definition = classification_definition + self.color_id = color_id + + +class UpdateClassificationRequestBodyDataStaticConfigField(BaseObject): + def __init__( + self, + *, + classification: Optional[ + UpdateClassificationRequestBodyDataStaticConfigClassificationField + ] = None, + **kwargs + ): + """ + :param classification: Additional details for the classification., defaults to None + :type classification: Optional[UpdateClassificationRequestBodyDataStaticConfigClassificationField], optional + """ + super().__init__(**kwargs) + self.classification = classification + + +class UpdateClassificationRequestBodyDataField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'static_config': 'staticConfig', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'staticConfig': 'static_config', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + key: str, + *, + static_config: Optional[ + UpdateClassificationRequestBodyDataStaticConfigField + ] = None, + **kwargs + ): + """ + :param key: A new label for the classification, as it will be + shown in the web and mobile interfaces. + :type key: str + :param static_config: A static configuration for the classification., defaults to None + :type static_config: Optional[UpdateClassificationRequestBodyDataStaticConfigField], optional + """ + super().__init__(**kwargs) + self.key = key + self.static_config = static_config + + +class UpdateClassificationRequestBody(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'field_key': 'fieldKey', + 'enum_option_key': 'enumOptionKey', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'fieldKey': 'field_key', + 'enumOptionKey': 'enum_option_key', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + enum_option_key: str, + data: UpdateClassificationRequestBodyDataField, + *, + op: UpdateClassificationRequestBodyOpField = UpdateClassificationRequestBodyOpField.EDITENUMOPTION, + field_key: UpdateClassificationRequestBodyFieldKeyField = UpdateClassificationRequestBodyFieldKeyField.BOX__SECURITY__CLASSIFICATION__KEY, + **kwargs + ): + """ + :param enum_option_key: The original label of the classification to change. + :type enum_option_key: str + :param data: The details of the updated classification. + :type data: UpdateClassificationRequestBodyDataField + :param op: The type of change to perform on the classification + object., defaults to UpdateClassificationRequestBodyOpField.EDITENUMOPTION + :type op: UpdateClassificationRequestBodyOpField, optional + :param field_key: Defines classifications + available in the enterprise., defaults to UpdateClassificationRequestBodyFieldKeyField.BOX__SECURITY__CLASSIFICATION__KEY + :type field_key: UpdateClassificationRequestBodyFieldKeyField, optional + """ + super().__init__(**kwargs) + self.enum_option_key = enum_option_key + self.data = data + self.op = op + self.field_key = field_key + + +class CreateClassificationTemplateScope(str, Enum): + ENTERPRISE = 'enterprise' + + +class CreateClassificationTemplateTemplateKey(str, Enum): + SECURITYCLASSIFICATION_6VMVOCHWUWO = 'securityClassification-6VMVochwUWo' + + +class CreateClassificationTemplateDisplayName(str, Enum): + CLASSIFICATION = 'Classification' + + +class CreateClassificationTemplateFieldsTypeField(str, Enum): + ENUM = 'enum' + + +class CreateClassificationTemplateFieldsKeyField(str, Enum): + BOX__SECURITY__CLASSIFICATION__KEY = 'Box__Security__Classification__Key' + + +class CreateClassificationTemplateFieldsDisplayNameField(str, Enum): + CLASSIFICATION = 'Classification' + + +class CreateClassificationTemplateFieldsOptionsStaticConfigClassificationField( + BaseObject +): + _fields_to_json_mapping: Dict[str, str] = { + 'classification_definition': 'classificationDefinition', + 'color_id': 'colorID', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'classificationDefinition': 'classification_definition', + 'colorID': 'color_id', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + classification_definition: Optional[str] = None, + color_id: Optional[int] = None, + **kwargs + ): + """ + :param classification_definition: A longer description of the classification., defaults to None + :type classification_definition: Optional[str], optional + :param color_id: An identifier used to assign a color to + a classification label. + + Mapping between a `colorID` and a color may + change without notice. Currently, the color + mappings are as follows. + + * `0`: Yellow. + * `1`: Orange. + * `2`: Watermelon red. + * `3`: Purple rain. + * `4`: Light blue. + * `5`: Dark blue. + * `6`: Light green. + * `7`: Gray., defaults to None + :type color_id: Optional[int], optional + """ + super().__init__(**kwargs) + self.classification_definition = classification_definition + self.color_id = color_id + + +class CreateClassificationTemplateFieldsOptionsStaticConfigField(BaseObject): + def __init__( + self, + *, + classification: Optional[ + CreateClassificationTemplateFieldsOptionsStaticConfigClassificationField + ] = None, + **kwargs + ): + """ + :param classification: Additional information about the classification., defaults to None + :type classification: Optional[CreateClassificationTemplateFieldsOptionsStaticConfigClassificationField], optional + """ + super().__init__(**kwargs) + self.classification = classification + + +class CreateClassificationTemplateFieldsOptionsField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'static_config': 'staticConfig', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'staticConfig': 'static_config', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + key: str, + *, + static_config: Optional[ + CreateClassificationTemplateFieldsOptionsStaticConfigField + ] = None, + **kwargs + ): + """ + :param key: The display name and key this classification. This + will be show in the Box UI. + :type key: str + :param static_config: Additional information about the classification., defaults to None + :type static_config: Optional[CreateClassificationTemplateFieldsOptionsStaticConfigField], optional + """ + super().__init__(**kwargs) + self.key = key + self.static_config = static_config + + +class CreateClassificationTemplateFields(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'display_name': 'displayName', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'displayName': 'display_name', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'enum'} + + def __init__( + self, + options: List[CreateClassificationTemplateFieldsOptionsField], + *, + type: CreateClassificationTemplateFieldsTypeField = CreateClassificationTemplateFieldsTypeField.ENUM, + key: CreateClassificationTemplateFieldsKeyField = CreateClassificationTemplateFieldsKeyField.BOX__SECURITY__CLASSIFICATION__KEY, + display_name: CreateClassificationTemplateFieldsDisplayNameField = CreateClassificationTemplateFieldsDisplayNameField.CLASSIFICATION, + hidden: Optional[bool] = None, + **kwargs + ): + """ + :param options: The actual list of classifications that are present on + this template. + :type options: List[CreateClassificationTemplateFieldsOptionsField] + :param type: The type of the field + that is always enum., defaults to CreateClassificationTemplateFieldsTypeField.ENUM + :type type: CreateClassificationTemplateFieldsTypeField, optional + :param key: Defines classifications + available in the enterprise., defaults to CreateClassificationTemplateFieldsKeyField.BOX__SECURITY__CLASSIFICATION__KEY + :type key: CreateClassificationTemplateFieldsKeyField, optional + :param display_name: A display name for the classification., defaults to CreateClassificationTemplateFieldsDisplayNameField.CLASSIFICATION + :type display_name: CreateClassificationTemplateFieldsDisplayNameField, optional + :param hidden: Determines if the classification + template is + hidden or available on + web and mobile + devices., defaults to None + :type hidden: Optional[bool], optional + """ + super().__init__(**kwargs) + self.options = options + self.type = type + self.key = key + self.display_name = display_name + self.hidden = hidden + + +class ClassificationsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_classification_template( + self, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ClassificationTemplate: + """ + Retrieves the classification metadata template and lists all the + + classifications available to this enterprise. + + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/metadata_templates/enterprise_12345/securityClassification-6VMVochwUWo/schema`. + + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/enterprise/securityClassification-6VMVochwUWo/schema', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ClassificationTemplate) + + def add_classification( + self, + request_body: List[AddClassificationRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ClassificationTemplate: + """ + Adds one or more new classifications to the list of classifications + + available to the enterprise. + + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/metadata_templates/enterprise_12345/securityClassification-6VMVochwUWo/schema`. + + :param request_body: Request body of addClassification method + :type request_body: List[AddClassificationRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/enterprise/securityClassification-6VMVochwUWo/schema#add', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ClassificationTemplate) + + def update_classification( + self, + request_body: List[UpdateClassificationRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ClassificationTemplate: + """ + Updates the labels and descriptions of one or more classifications + + available to the enterprise. + + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/metadata_templates/enterprise_12345/securityClassification-6VMVochwUWo/schema`. + + :param request_body: Request body of updateClassification method + :type request_body: List[UpdateClassificationRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/enterprise/securityClassification-6VMVochwUWo/schema#update', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json-patch+json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ClassificationTemplate) + + def create_classification_template( + self, + fields: List[CreateClassificationTemplateFields], + *, + scope: CreateClassificationTemplateScope = CreateClassificationTemplateScope.ENTERPRISE, + template_key: CreateClassificationTemplateTemplateKey = CreateClassificationTemplateTemplateKey.SECURITYCLASSIFICATION_6VMVOCHWUWO, + display_name: CreateClassificationTemplateDisplayName = CreateClassificationTemplateDisplayName.CLASSIFICATION, + hidden: Optional[bool] = None, + copy_instance_on_item_copy: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ClassificationTemplate: + """ + When an enterprise does not yet have any classifications, this API call + + initializes the classification template with an initial set of + + + classifications. + + + If an enterprise already has a classification, the template will already + + + exist and instead an API call should be made to add additional + + + classifications. + + :param fields: The classification template requires exactly + one field, which holds + all the valid classification values. + :type fields: List[CreateClassificationTemplateFields] + :param scope: The scope in which to create the classifications. This should + be `enterprise` or `enterprise_{id}` where `id` is the unique + ID of the enterprise., defaults to CreateClassificationTemplateScope.ENTERPRISE + :type scope: CreateClassificationTemplateScope, optional + :param template_key: Defines the list of metadata templates., defaults to CreateClassificationTemplateTemplateKey.SECURITYCLASSIFICATION_6VMVOCHWUWO + :type template_key: CreateClassificationTemplateTemplateKey, optional + :param display_name: The name of the + template as shown in web and mobile interfaces., defaults to CreateClassificationTemplateDisplayName.CLASSIFICATION + :type display_name: CreateClassificationTemplateDisplayName, optional + :param hidden: Determines if the classification template is + hidden or available on web and mobile + devices., defaults to None + :type hidden: Optional[bool], optional + :param copy_instance_on_item_copy: Determines if classifications are + copied along when the file or folder is + copied., defaults to None + :type copy_instance_on_item_copy: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'scope': scope, + 'templateKey': template_key, + 'displayName': display_name, + 'hidden': hidden, + 'copyInstanceOnItemCopy': copy_instance_on_item_copy, + 'fields': fields, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/schema#classifications', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ClassificationTemplate) diff --git a/box_sdk_gen/managers/collaboration_allowlist_entries.py b/box_sdk_gen/managers/collaboration_allowlist_entries.py new file mode 100644 index 000000000..15176d4fe --- /dev/null +++ b/box_sdk_gen/managers/collaboration_allowlist_entries.py @@ -0,0 +1,226 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.collaboration_allowlist_entries import ( + CollaborationAllowlistEntries, +) + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.collaboration_allowlist_entry import ( + CollaborationAllowlistEntry, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateCollaborationWhitelistEntryDirection(str, Enum): + INBOUND = 'inbound' + OUTBOUND = 'outbound' + BOTH = 'both' + + +class CollaborationAllowlistEntriesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_collaboration_whitelist_entries( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationAllowlistEntries: + """ + Returns the list domains that have been deemed safe to create collaborations + + for within the current enterprise. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_entries', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationAllowlistEntries) + + def create_collaboration_whitelist_entry( + self, + domain: str, + direction: CreateCollaborationWhitelistEntryDirection, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationAllowlistEntry: + """ + Creates a new entry in the list of allowed domains to allow + + collaboration for. + + :param domain: The domain to add to the list of allowed domains. + :type domain: str + :param direction: The direction in which to allow collaborations. + :type direction: CreateCollaborationWhitelistEntryDirection + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'domain': domain, 'direction': direction} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_entries', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationAllowlistEntry) + + def get_collaboration_whitelist_entry_by_id( + self, + collaboration_whitelist_entry_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationAllowlistEntry: + """ + Returns a domain that has been deemed safe to create collaborations + + for within the current enterprise. + + :param collaboration_whitelist_entry_id: The ID of the entry in the list. + Example: "213123" + :type collaboration_whitelist_entry_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_entries/', + to_string(collaboration_whitelist_entry_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationAllowlistEntry) + + def delete_collaboration_whitelist_entry_by_id( + self, + collaboration_whitelist_entry_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes a domain from the list of domains that have been deemed safe to create + + collaborations for within the current enterprise. + + :param collaboration_whitelist_entry_id: The ID of the entry in the list. + Example: "213123" + :type collaboration_whitelist_entry_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_entries/', + to_string(collaboration_whitelist_entry_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/collaboration_allowlist_exempt_targets.py b/box_sdk_gen/managers/collaboration_allowlist_exempt_targets.py new file mode 100644 index 000000000..aaacce06f --- /dev/null +++ b/box_sdk_gen/managers/collaboration_allowlist_exempt_targets.py @@ -0,0 +1,227 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.collaboration_allowlist_exempt_targets import ( + CollaborationAllowlistExemptTargets, +) + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.collaboration_allowlist_exempt_target import ( + CollaborationAllowlistExemptTarget, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateCollaborationWhitelistExemptTargetUser(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of the user to exempt. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class CollaborationAllowlistExemptTargetsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_collaboration_whitelist_exempt_targets( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationAllowlistExemptTargets: + """ + Returns a list of users who have been exempt from the collaboration + + domain restrictions. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_exempt_targets', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationAllowlistExemptTargets) + + def create_collaboration_whitelist_exempt_target( + self, + user: CreateCollaborationWhitelistExemptTargetUser, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationAllowlistExemptTarget: + """ + Exempts a user from the restrictions set out by the allowed list of domains + + for collaborations. + + :param user: The user to exempt. + :type user: CreateCollaborationWhitelistExemptTargetUser + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'user': user} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_exempt_targets', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationAllowlistExemptTarget) + + def get_collaboration_whitelist_exempt_target_by_id( + self, + collaboration_whitelist_exempt_target_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationAllowlistExemptTarget: + """ + Returns a users who has been exempt from the collaboration + + domain restrictions. + + :param collaboration_whitelist_exempt_target_id: The ID of the exemption to the list. + Example: "984923" + :type collaboration_whitelist_exempt_target_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_exempt_targets/', + to_string(collaboration_whitelist_exempt_target_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationAllowlistExemptTarget) + + def delete_collaboration_whitelist_exempt_target_by_id( + self, + collaboration_whitelist_exempt_target_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes a user's exemption from the restrictions set out by the allowed list + + of domains for collaborations. + + :param collaboration_whitelist_exempt_target_id: The ID of the exemption to the list. + Example: "984923" + :type collaboration_whitelist_exempt_target_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaboration_whitelist_exempt_targets/', + to_string(collaboration_whitelist_exempt_target_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/collections.py b/box_sdk_gen/managers/collections.py new file mode 100644 index 000000000..bb060a8d3 --- /dev/null +++ b/box_sdk_gen/managers/collections.py @@ -0,0 +1,217 @@ +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.collections import Collections + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.items_offset_paginated import ItemsOffsetPaginated + +from box_sdk_gen.schemas.collection import Collection + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CollectionsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_collections( + self, + *, + fields: Optional[List[str]] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Collections: + """ + Retrieves all collections for a given user. + + Currently, only the `favorites` collection + + + is supported. + + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'offset': to_string(offset), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/collections'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Collections) + + def get_collection_items( + self, + collection_id: str, + *, + fields: Optional[List[str]] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ItemsOffsetPaginated: + """ + Retrieves the files and/or folders contained within + + this collection. + + :param collection_id: The ID of the collection. + Example: "926489" + :type collection_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'offset': to_string(offset), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collections/', + to_string(collection_id), + '/items', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ItemsOffsetPaginated) + + def get_collection_by_id( + self, + collection_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Collection: + """ + Retrieves a collection by its ID. + :param collection_id: The ID of the collection. + Example: "926489" + :type collection_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collections/', + to_string(collection_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Collection) diff --git a/box_sdk_gen/managers/comments.py b/box_sdk_gen/managers/comments.py new file mode 100644 index 000000000..f09708a1b --- /dev/null +++ b/box_sdk_gen/managers/comments.py @@ -0,0 +1,357 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.comments import Comments + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.comment_full import CommentFull + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateCommentItemTypeField(str, Enum): + FILE = 'file' + COMMENT = 'comment' + + +class CreateCommentItem(BaseObject): + _discriminator = 'type', {'file', 'comment'} + + def __init__(self, id: str, type: CreateCommentItemTypeField, **kwargs): + """ + :param id: The ID of the item. + :type id: str + :param type: The type of the item that this comment will be placed on. + :type type: CreateCommentItemTypeField + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CommentsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_comments( + self, + file_id: str, + *, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Comments: + """ + Retrieves a list of comments for a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'limit': to_string(limit), + 'offset': to_string(offset), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/comments', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Comments) + + def get_comment_by_id( + self, + comment_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CommentFull: + """ + Retrieves the message and metadata for a specific comment, as well + + as information on the user who created the comment. + + :param comment_id: The ID of the comment. + Example: "12345" + :type comment_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/comments/', + to_string(comment_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CommentFull) + + def update_comment_by_id( + self, + comment_id: str, + *, + message: Optional[str] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CommentFull: + """ + Update the message of a comment. + :param comment_id: The ID of the comment. + Example: "12345" + :type comment_id: str + :param message: The text of the comment to update., defaults to None + :type message: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'message': message} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/comments/', + to_string(comment_id), + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CommentFull) + + def delete_comment_by_id( + self, + comment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Permanently deletes a comment. + :param comment_id: The ID of the comment. + Example: "12345" + :type comment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/comments/', + to_string(comment_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def create_comment( + self, + message: str, + item: CreateCommentItem, + *, + tagged_message: Optional[str] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CommentFull: + """ + Adds a comment by the user to a specific file, or + + as a reply to an other comment. + + :param message: The text of the comment. + + To mention a user, use the `tagged_message` + parameter instead. + :type message: str + :param item: The item to attach the comment to. + :type item: CreateCommentItem + :param tagged_message: The text of the comment, including `@[user_id:name]` + somewhere in the message to mention another user, which + will send them an email notification, letting them know + they have been mentioned. + + The `user_id` is the target user's ID, where the `name` + can be any custom phrase. In the Box UI this name will + link to the user's profile. + + If you are not mentioning another user, use `message` + instead., defaults to None + :type tagged_message: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'message': message, + 'tagged_message': tagged_message, + 'item': item, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/comments']), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CommentFull) diff --git a/box_sdk_gen/managers/device_pinners.py b/box_sdk_gen/managers/device_pinners.py new file mode 100644 index 000000000..612289415 --- /dev/null +++ b/box_sdk_gen/managers/device_pinners.py @@ -0,0 +1,188 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.device_pinner import DevicePinner + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.device_pinners import DevicePinners + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetEnterpriseDevicePinnersDirection(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class DevicePinnersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_device_pinner_by_id( + self, + device_pinner_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DevicePinner: + """ + Retrieves information about an individual device pin. + :param device_pinner_id: The ID of the device pin. + Example: "2324234" + :type device_pinner_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/device_pinners/', + to_string(device_pinner_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DevicePinner) + + def delete_device_pinner_by_id( + self, + device_pinner_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes an individual device pin. + :param device_pinner_id: The ID of the device pin. + Example: "2324234" + :type device_pinner_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/device_pinners/', + to_string(device_pinner_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_enterprise_device_pinners( + self, + enterprise_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + direction: Optional[GetEnterpriseDevicePinnersDirection] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DevicePinners: + """ + Retrieves all the device pins within an enterprise. + + The user must have admin privileges, and the application + + + needs the "manage enterprise" scope to make this call. + + :param enterprise_id: The ID of the enterprise. + Example: "3442311" + :type enterprise_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param direction: The direction to sort results in. This can be either in alphabetical ascending + (`ASC`) or descending (`DESC`) order., defaults to None + :type direction: Optional[GetEnterpriseDevicePinnersDirection], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'marker': to_string(marker), + 'limit': to_string(limit), + 'direction': to_string(direction), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/enterprises/', + to_string(enterprise_id), + '/device_pinners', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DevicePinners) diff --git a/box_sdk_gen/managers/docgen.py b/box_sdk_gen/managers/docgen.py new file mode 100644 index 000000000..b5aad98a2 --- /dev/null +++ b/box_sdk_gen/managers/docgen.py @@ -0,0 +1,297 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from typing import List + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.file_version_base_v2025_r0 import ( + FileVersionBaseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_document_generation_data_v2025_r0 import ( + DocGenDocumentGenerationDataV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_v2025_r0 import DocGenJobV2025R0 + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_jobs_full_v2025_r0 import ( + DocGenJobsFullV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_jobs_v2025_r0 import DocGenJobsV2025R0 + +from box_sdk_gen.schemas.v2025_r0.doc_gen_batch_base_v2025_r0 import ( + DocGenBatchBaseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_batch_create_request_v2025_r0 import ( + DocGenBatchCreateRequestV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateDocgenBatchV2025R0DestinationFolderTypeField(str, Enum): + FOLDER = 'folder' + + +class CreateDocgenBatchV2025R0DestinationFolder(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + type: CreateDocgenBatchV2025R0DestinationFolderTypeField = CreateDocgenBatchV2025R0DestinationFolderTypeField.FOLDER, + **kwargs + ): + """ + :param id: ID of the folder. + :type id: str + :param type: The value will always be `folder`., defaults to CreateDocgenBatchV2025R0DestinationFolderTypeField.FOLDER + :type type: CreateDocgenBatchV2025R0DestinationFolderTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class DocgenManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_docgen_job_by_id_v2025_r0( + self, + job_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenJobV2025R0: + """ + Get details of the Box Doc Gen job. + :param job_id: Box Doc Gen job ID. + Example: 123 + :type job_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/docgen_jobs/', + to_string(job_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenJobV2025R0) + + def get_docgen_jobs_v2025_r0( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenJobsFullV2025R0: + """ + Lists all Box Doc Gen jobs for a user. + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/docgen_jobs'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenJobsFullV2025R0) + + def get_docgen_batch_job_by_id_v2025_r0( + self, + batch_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenJobsV2025R0: + """ + Lists Box Doc Gen jobs in a batch. + :param batch_id: Box Doc Gen batch ID. + Example: 123 + :type batch_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/docgen_batch_jobs/', + to_string(batch_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenJobsV2025R0) + + def create_docgen_batch_v2025_r0( + self, + file: FileReferenceV2025R0, + input_source: str, + destination_folder: CreateDocgenBatchV2025R0DestinationFolder, + output_type: str, + document_generation_data: List[DocGenDocumentGenerationDataV2025R0], + *, + file_version: Optional[FileVersionBaseV2025R0] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenBatchBaseV2025R0: + """ + Generates a document using a Box Doc Gen template. + :param input_source: Source of input. The value has to be `api` for all the API-based document generation requests. + :type input_source: str + :param output_type: Type of the output file. + :type output_type: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'file': file, + 'file_version': file_version, + 'input_source': input_source, + 'destination_folder': destination_folder, + 'output_type': output_type, + 'document_generation_data': document_generation_data, + } + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/docgen_batches'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenBatchBaseV2025R0) diff --git a/box_sdk_gen/managers/docgen_template.py b/box_sdk_gen/managers/docgen_template.py new file mode 100644 index 000000000..203f2b097 --- /dev/null +++ b/box_sdk_gen/managers/docgen_template.py @@ -0,0 +1,349 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_base_v2025_r0 import ( + DocGenTemplateBaseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_create_request_v2025_r0 import ( + DocGenTemplateCreateRequestV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_templates_v2025_r0 import ( + DocGenTemplatesV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_v2025_r0 import DocGenTemplateV2025R0 + +from box_sdk_gen.schemas.v2025_r0.doc_gen_tags_v2025_r0 import DocGenTagsV2025R0 + +from box_sdk_gen.schemas.v2025_r0.doc_gen_tags_processing_message_v2025_r0 import ( + DocGenTagsProcessingMessageV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_jobs_v2025_r0 import DocGenJobsV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class DocgenTemplateManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def create_docgen_template_v2025_r0( + self, + file: FileReferenceV2025R0, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenTemplateBaseV2025R0: + """ + Marks a file as a Box Doc Gen template. + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'file': file} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/docgen_templates'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenTemplateBaseV2025R0) + + def get_docgen_templates_v2025_r0( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenTemplatesV2025R0: + """ + Lists Box Doc Gen templates on which the user is a collaborator. + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/docgen_templates'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenTemplatesV2025R0) + + def delete_docgen_template_by_id_v2025_r0( + self, + template_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Unmarks file as Box Doc Gen template. + :param template_id: ID of the file which will no longer be marked as a Box Doc Gen template. + Example: "123" + :type template_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/docgen_templates/', + to_string(template_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_docgen_template_by_id_v2025_r0( + self, + template_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenTemplateV2025R0: + """ + Lists details of a specific Box Doc Gen template. + :param template_id: The ID of a Box Doc Gen template. + Example: 123 + :type template_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/docgen_templates/', + to_string(template_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenTemplateV2025R0) + + def get_docgen_template_tags_v2025_r0( + self, + template_id: str, + *, + template_version_id: Optional[str] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenTagsV2025R0: + """ + Lists all tags in a Box Doc Gen template. + :param template_id: ID of template. + Example: 123 + :type template_id: str + :param template_version_id: Id of template version., defaults to None + :type template_version_id: Optional[str], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'template_version_id': to_string(template_version_id), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/docgen_templates/', + to_string(template_id), + '/tags', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenTagsV2025R0) + + def get_docgen_template_job_by_id_v2025_r0( + self, + template_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> DocGenJobsV2025R0: + """ + Lists the users jobs which use this template. + :param template_id: Id of template to fetch jobs for. + Example: 123 + :type template_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/docgen_template_jobs/', + to_string(template_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, DocGenJobsV2025R0) diff --git a/box_sdk_gen/managers/downloads.py b/box_sdk_gen/managers/downloads.py new file mode 100644 index 000000000..c5bde893c --- /dev/null +++ b/box_sdk_gen/managers/downloads.py @@ -0,0 +1,256 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.internal.utils import write_input_stream_to_output_stream + +from box_sdk_gen.internal.utils import OutputStream + + +class DownloadsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_download_file_url( + self, + file_id: str, + *, + version: Optional[str] = None, + access_token: Optional[str] = None, + range: Optional[str] = None, + boxapi: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> str: + """ + Returns the contents of a file in binary format. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param version: The file version to download., defaults to None + :type version: Optional[str], optional + :param access_token: An optional access token that can be used to pre-authenticate this request, which means that a download link can be shared with a browser or a third party service without them needing to know how to handle the authentication. + When using this parameter, please make sure that the access token is sufficiently scoped down to only allow read access to that file and no other files or folders., defaults to None + :type access_token: Optional[str], optional + :param range: The byte range of the content to download. + + The format `bytes={start_byte}-{end_byte}` can be used to specify + what section of the file to download., defaults to None + :type range: Optional[str], optional + :param boxapi: The URL, and optional password, for the shared link of this item. + + This header can be used to access items that have not been + explicitly shared with a user. + + Use the format `shared_link=[link]` or if a password is required then + use `shared_link=[link]&shared_link_password=[password]`. + + This header can be used on the file or folder shared, as well as on any files + or folders nested within the item., defaults to None + :type boxapi: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'version': to_string(version), 'access_token': to_string(access_token)} + ) + headers_map: Dict[str, str] = prepare_params( + {'range': to_string(range), 'boxapi': to_string(boxapi), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/content', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + follow_redirects=False, + ) + ) + if 'location' in response.headers: + return response.headers.get('location') + if 'Location' in response.headers: + return response.headers.get('Location') + raise BoxSDKError(message='No location header in response') + + def download_file( + self, + file_id: str, + *, + version: Optional[str] = None, + access_token: Optional[str] = None, + range: Optional[str] = None, + boxapi: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Optional[ByteStream]: + """ + Returns the contents of a file in binary format. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param version: The file version to download., defaults to None + :type version: Optional[str], optional + :param access_token: An optional access token that can be used to pre-authenticate this request, which means that a download link can be shared with a browser or a third party service without them needing to know how to handle the authentication. + When using this parameter, please make sure that the access token is sufficiently scoped down to only allow read access to that file and no other files or folders., defaults to None + :type access_token: Optional[str], optional + :param range: The byte range of the content to download. + + The format `bytes={start_byte}-{end_byte}` can be used to specify + what section of the file to download., defaults to None + :type range: Optional[str], optional + :param boxapi: The URL, and optional password, for the shared link of this item. + + This header can be used to access items that have not been + explicitly shared with a user. + + Use the format `shared_link=[link]` or if a password is required then + use `shared_link=[link]&shared_link_password=[password]`. + + This header can be used on the file or folder shared, as well as on any files + or folders nested within the item., defaults to None + :type boxapi: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'version': to_string(version), 'access_token': to_string(access_token)} + ) + headers_map: Dict[str, str] = prepare_params( + {'range': to_string(range), 'boxapi': to_string(boxapi), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/content', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.BINARY, + auth=self.auth, + network_session=self.network_session, + ) + ) + if to_string(response.status) == '202': + return None + return response.content + + def download_file_to_output_stream( + self, + file_id: str, + output_stream: OutputStream, + *, + version: Optional[str] = None, + access_token: Optional[str] = None, + range: Optional[str] = None, + boxapi: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param output_stream: Download file to a given output stream + :type output_stream: OutputStream + :param version: The file version to download., defaults to None + :type version: Optional[str], optional + :param access_token: An optional access token that can be used to pre-authenticate this request, which means that a download link can be shared with a browser or a third party service without them needing to know how to handle the authentication. + When using this parameter, please make sure that the access token is sufficiently scoped down to only allow read access to that file and no other files or folders., defaults to None + :type access_token: Optional[str], optional + :param range: The byte range of the content to download. + + The format `bytes={start_byte}-{end_byte}` can be used to specify + what section of the file to download., defaults to None + :type range: Optional[str], optional + :param boxapi: The URL, and optional password, for the shared link of this item. + + This header can be used to access items that have not been + explicitly shared with a user. + + Use the format `shared_link=[link]` or if a password is required then + use `shared_link=[link]&shared_link_password=[password]`. + + This header can be used on the file or folder shared, as well as on any files + or folders nested within the item., defaults to None + :type boxapi: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + download_stream: ByteStream = self.download_file( + file_id, + version=version, + access_token=access_token, + range=range, + boxapi=boxapi, + extra_headers=extra_headers, + ) + write_input_stream_to_output_stream(download_stream, output_stream) diff --git a/box_sdk_gen/managers/email_aliases.py b/box_sdk_gen/managers/email_aliases.py new file mode 100644 index 000000000..6d837b116 --- /dev/null +++ b/box_sdk_gen/managers/email_aliases.py @@ -0,0 +1,175 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.email_aliases import EmailAliases + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.email_alias import EmailAlias + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class EmailAliasesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_user_email_aliases( + self, user_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> EmailAliases: + """ + Retrieves all email aliases for a user. The collection + + does not include the primary login for the user. + + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/email_aliases', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, EmailAliases) + + def create_user_email_alias( + self, + user_id: str, + email: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> EmailAlias: + """ + Adds a new email alias to a user account.. + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param email: The email address to add to the account as an alias. + + Note: The domain of the email alias needs to be registered + to your enterprise. + See the [domain verification guide]( + https://support.box.com/hc/en-us/articles/4408619650579-Domain-Verification + ) for steps to add a new domain. + :type email: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'email': email} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/email_aliases', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, EmailAlias) + + def delete_user_email_alias_by_id( + self, + user_id: str, + email_alias_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes an email alias from a user. + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param email_alias_id: The ID of the email alias. + Example: "23432" + :type email_alias_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/email_aliases/', + to_string(email_alias_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/events.py b/box_sdk_gen/managers/events.py new file mode 100644 index 000000000..1ccd76a85 --- /dev/null +++ b/box_sdk_gen/managers/events.py @@ -0,0 +1,641 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.realtime_servers import RealtimeServers + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.events import Events + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.box.event_stream import EventStream + + +class GetEventsStreamType(str, Enum): + ALL = 'all' + CHANGES = 'changes' + SYNC = 'sync' + ADMIN_LOGS = 'admin_logs' + ADMIN_LOGS_STREAMING = 'admin_logs_streaming' + + +class GetEventsEventType(str, Enum): + ACCESS_GRANTED = 'ACCESS_GRANTED' + ACCESS_REVOKED = 'ACCESS_REVOKED' + ADD_DEVICE_ASSOCIATION = 'ADD_DEVICE_ASSOCIATION' + ADD_LOGIN_ACTIVITY_DEVICE = 'ADD_LOGIN_ACTIVITY_DEVICE' + ADMIN_LOGIN = 'ADMIN_LOGIN' + APPLICATION_CREATED = 'APPLICATION_CREATED' + APPLICATION_PUBLIC_KEY_ADDED = 'APPLICATION_PUBLIC_KEY_ADDED' + APPLICATION_PUBLIC_KEY_DELETED = 'APPLICATION_PUBLIC_KEY_DELETED' + CHANGE_ADMIN_ROLE = 'CHANGE_ADMIN_ROLE' + CHANGE_FOLDER_PERMISSION = 'CHANGE_FOLDER_PERMISSION' + COLLABORATION_ACCEPT = 'COLLABORATION_ACCEPT' + COLLABORATION_EXPIRATION = 'COLLABORATION_EXPIRATION' + COLLABORATION_INVITE = 'COLLABORATION_INVITE' + COLLABORATION_REMOVE = 'COLLABORATION_REMOVE' + COLLABORATION_ROLE_CHANGE = 'COLLABORATION_ROLE_CHANGE' + COMMENT_CREATE = 'COMMENT_CREATE' + COMMENT_DELETE = 'COMMENT_DELETE' + CONTENT_WORKFLOW_ABNORMAL_DOWNLOAD_ACTIVITY = ( + 'CONTENT_WORKFLOW_ABNORMAL_DOWNLOAD_ACTIVITY' + ) + CONTENT_WORKFLOW_AUTOMATION_ADD = 'CONTENT_WORKFLOW_AUTOMATION_ADD' + CONTENT_WORKFLOW_AUTOMATION_DELETE = 'CONTENT_WORKFLOW_AUTOMATION_DELETE' + CONTENT_WORKFLOW_POLICY_ADD = 'CONTENT_WORKFLOW_POLICY_ADD' + CONTENT_WORKFLOW_SHARING_POLICY_VIOLATION = ( + 'CONTENT_WORKFLOW_SHARING_POLICY_VIOLATION' + ) + CONTENT_WORKFLOW_UPLOAD_POLICY_VIOLATION = ( + 'CONTENT_WORKFLOW_UPLOAD_POLICY_VIOLATION' + ) + COPY = 'COPY' + DATA_RETENTION_CREATE_RETENTION = 'DATA_RETENTION_CREATE_RETENTION' + DATA_RETENTION_REMOVE_RETENTION = 'DATA_RETENTION_REMOVE_RETENTION' + DELETE = 'DELETE' + DELETE_USER = 'DELETE_USER' + DEVICE_TRUST_CHECK_FAILED = 'DEVICE_TRUST_CHECK_FAILED' + DOWNLOAD = 'DOWNLOAD' + EDIT = 'EDIT' + EDIT_USER = 'EDIT_USER' + EMAIL_ALIAS_CONFIRM = 'EMAIL_ALIAS_CONFIRM' + EMAIL_ALIAS_REMOVE = 'EMAIL_ALIAS_REMOVE' + ENTERPRISE_APP_AUTHORIZATION_UPDATE = 'ENTERPRISE_APP_AUTHORIZATION_UPDATE' + EXTERNAL_COLLAB_SECURITY_SETTINGS = 'EXTERNAL_COLLAB_SECURITY_SETTINGS' + FAILED_LOGIN = 'FAILED_LOGIN' + FILE_MARKED_MALICIOUS = 'FILE_MARKED_MALICIOUS' + FILE_WATERMARKED_DOWNLOAD = 'FILE_WATERMARKED_DOWNLOAD' + GROUP_ADD_ITEM = 'GROUP_ADD_ITEM' + GROUP_ADD_USER = 'GROUP_ADD_USER' + GROUP_CREATION = 'GROUP_CREATION' + GROUP_DELETION = 'GROUP_DELETION' + GROUP_EDITED = 'GROUP_EDITED' + GROUP_REMOVE_ITEM = 'GROUP_REMOVE_ITEM' + GROUP_REMOVE_USER = 'GROUP_REMOVE_USER' + ITEM_EMAIL_SEND = 'ITEM_EMAIL_SEND' + ITEM_MODIFY = 'ITEM_MODIFY' + ITEM_OPEN = 'ITEM_OPEN' + ITEM_SHARED_UPDATE = 'ITEM_SHARED_UPDATE' + ITEM_SYNC = 'ITEM_SYNC' + ITEM_UNSYNC = 'ITEM_UNSYNC' + LEGAL_HOLD_ASSIGNMENT_CREATE = 'LEGAL_HOLD_ASSIGNMENT_CREATE' + LEGAL_HOLD_ASSIGNMENT_DELETE = 'LEGAL_HOLD_ASSIGNMENT_DELETE' + LEGAL_HOLD_POLICY_CREATE = 'LEGAL_HOLD_POLICY_CREATE' + LEGAL_HOLD_POLICY_DELETE = 'LEGAL_HOLD_POLICY_DELETE' + LEGAL_HOLD_POLICY_UPDATE = 'LEGAL_HOLD_POLICY_UPDATE' + LOCK = 'LOCK' + LOGIN = 'LOGIN' + METADATA_INSTANCE_CREATE = 'METADATA_INSTANCE_CREATE' + METADATA_INSTANCE_DELETE = 'METADATA_INSTANCE_DELETE' + METADATA_INSTANCE_UPDATE = 'METADATA_INSTANCE_UPDATE' + METADATA_TEMPLATE_CREATE = 'METADATA_TEMPLATE_CREATE' + METADATA_TEMPLATE_DELETE = 'METADATA_TEMPLATE_DELETE' + METADATA_TEMPLATE_UPDATE = 'METADATA_TEMPLATE_UPDATE' + MOVE = 'MOVE' + NEW_USER = 'NEW_USER' + OAUTH2_ACCESS_TOKEN_REVOKE = 'OAUTH2_ACCESS_TOKEN_REVOKE' + PREVIEW = 'PREVIEW' + REMOVE_DEVICE_ASSOCIATION = 'REMOVE_DEVICE_ASSOCIATION' + REMOVE_LOGIN_ACTIVITY_DEVICE = 'REMOVE_LOGIN_ACTIVITY_DEVICE' + RENAME = 'RENAME' + RETENTION_POLICY_ASSIGNMENT_ADD = 'RETENTION_POLICY_ASSIGNMENT_ADD' + SHARE = 'SHARE' + SHARED_LINK_SEND = 'SHARED_LINK_SEND' + SHARE_EXPIRATION = 'SHARE_EXPIRATION' + SHIELD_ALERT = 'SHIELD_ALERT' + SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED = 'SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED' + SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION = ( + 'SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION' + ) + SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED = 'SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED' + SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION = ( + 'SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION' + ) + SHIELD_JUSTIFICATION_APPROVAL = 'SHIELD_JUSTIFICATION_APPROVAL' + SHIELD_SHARED_LINK_ACCESS_BLOCKED = 'SHIELD_SHARED_LINK_ACCESS_BLOCKED' + SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_CREATE = ( + 'SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_CREATE' + ) + SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_UPDATE = ( + 'SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_UPDATE' + ) + SIGN_DOCUMENT_ASSIGNED = 'SIGN_DOCUMENT_ASSIGNED' + SIGN_DOCUMENT_CANCELLED = 'SIGN_DOCUMENT_CANCELLED' + SIGN_DOCUMENT_COMPLETED = 'SIGN_DOCUMENT_COMPLETED' + SIGN_DOCUMENT_CONVERTED = 'SIGN_DOCUMENT_CONVERTED' + SIGN_DOCUMENT_CREATED = 'SIGN_DOCUMENT_CREATED' + SIGN_DOCUMENT_DECLINED = 'SIGN_DOCUMENT_DECLINED' + SIGN_DOCUMENT_EXPIRED = 'SIGN_DOCUMENT_EXPIRED' + SIGN_DOCUMENT_SIGNED = 'SIGN_DOCUMENT_SIGNED' + SIGN_DOCUMENT_VIEWED_BY_SIGNED = 'SIGN_DOCUMENT_VIEWED_BY_SIGNED' + SIGNER_DOWNLOADED = 'SIGNER_DOWNLOADED' + SIGNER_FORWARDED = 'SIGNER_FORWARDED' + STORAGE_EXPIRATION = 'STORAGE_EXPIRATION' + TASK_ASSIGNMENT_CREATE = 'TASK_ASSIGNMENT_CREATE' + TASK_ASSIGNMENT_DELETE = 'TASK_ASSIGNMENT_DELETE' + TASK_ASSIGNMENT_UPDATE = 'TASK_ASSIGNMENT_UPDATE' + TASK_CREATE = 'TASK_CREATE' + TASK_UPDATE = 'TASK_UPDATE' + TERMS_OF_SERVICE_ACCEPT = 'TERMS_OF_SERVICE_ACCEPT' + TERMS_OF_SERVICE_REJECT = 'TERMS_OF_SERVICE_REJECT' + UNDELETE = 'UNDELETE' + UNLOCK = 'UNLOCK' + UNSHARE = 'UNSHARE' + UPDATE_COLLABORATION_EXPIRATION = 'UPDATE_COLLABORATION_EXPIRATION' + UPDATE_SHARE_EXPIRATION = 'UPDATE_SHARE_EXPIRATION' + UPLOAD = 'UPLOAD' + USER_AUTHENTICATE_OAUTH2_ACCESS_TOKEN_CREATE = ( + 'USER_AUTHENTICATE_OAUTH2_ACCESS_TOKEN_CREATE' + ) + WATERMARK_LABEL_CREATE = 'WATERMARK_LABEL_CREATE' + WATERMARK_LABEL_DELETE = 'WATERMARK_LABEL_DELETE' + + +class GetEventStreamQueryParamsStreamTypeField(str, Enum): + ALL = 'all' + CHANGES = 'changes' + SYNC = 'sync' + ADMIN_LOGS = 'admin_logs' + ADMIN_LOGS_STREAMING = 'admin_logs_streaming' + + +class GetEventStreamQueryParamsEventTypeField(str, Enum): + ACCESS_GRANTED = 'ACCESS_GRANTED' + ACCESS_REVOKED = 'ACCESS_REVOKED' + ADD_DEVICE_ASSOCIATION = 'ADD_DEVICE_ASSOCIATION' + ADD_LOGIN_ACTIVITY_DEVICE = 'ADD_LOGIN_ACTIVITY_DEVICE' + ADMIN_LOGIN = 'ADMIN_LOGIN' + APPLICATION_CREATED = 'APPLICATION_CREATED' + APPLICATION_PUBLIC_KEY_ADDED = 'APPLICATION_PUBLIC_KEY_ADDED' + APPLICATION_PUBLIC_KEY_DELETED = 'APPLICATION_PUBLIC_KEY_DELETED' + CHANGE_ADMIN_ROLE = 'CHANGE_ADMIN_ROLE' + CHANGE_FOLDER_PERMISSION = 'CHANGE_FOLDER_PERMISSION' + COLLABORATION_ACCEPT = 'COLLABORATION_ACCEPT' + COLLABORATION_EXPIRATION = 'COLLABORATION_EXPIRATION' + COLLABORATION_INVITE = 'COLLABORATION_INVITE' + COLLABORATION_REMOVE = 'COLLABORATION_REMOVE' + COLLABORATION_ROLE_CHANGE = 'COLLABORATION_ROLE_CHANGE' + COMMENT_CREATE = 'COMMENT_CREATE' + COMMENT_DELETE = 'COMMENT_DELETE' + CONTENT_WORKFLOW_ABNORMAL_DOWNLOAD_ACTIVITY = ( + 'CONTENT_WORKFLOW_ABNORMAL_DOWNLOAD_ACTIVITY' + ) + CONTENT_WORKFLOW_AUTOMATION_ADD = 'CONTENT_WORKFLOW_AUTOMATION_ADD' + CONTENT_WORKFLOW_AUTOMATION_DELETE = 'CONTENT_WORKFLOW_AUTOMATION_DELETE' + CONTENT_WORKFLOW_POLICY_ADD = 'CONTENT_WORKFLOW_POLICY_ADD' + CONTENT_WORKFLOW_SHARING_POLICY_VIOLATION = ( + 'CONTENT_WORKFLOW_SHARING_POLICY_VIOLATION' + ) + CONTENT_WORKFLOW_UPLOAD_POLICY_VIOLATION = ( + 'CONTENT_WORKFLOW_UPLOAD_POLICY_VIOLATION' + ) + COPY = 'COPY' + DATA_RETENTION_CREATE_RETENTION = 'DATA_RETENTION_CREATE_RETENTION' + DATA_RETENTION_REMOVE_RETENTION = 'DATA_RETENTION_REMOVE_RETENTION' + DELETE = 'DELETE' + DELETE_USER = 'DELETE_USER' + DEVICE_TRUST_CHECK_FAILED = 'DEVICE_TRUST_CHECK_FAILED' + DOWNLOAD = 'DOWNLOAD' + EDIT = 'EDIT' + EDIT_USER = 'EDIT_USER' + EMAIL_ALIAS_CONFIRM = 'EMAIL_ALIAS_CONFIRM' + EMAIL_ALIAS_REMOVE = 'EMAIL_ALIAS_REMOVE' + ENTERPRISE_APP_AUTHORIZATION_UPDATE = 'ENTERPRISE_APP_AUTHORIZATION_UPDATE' + EXTERNAL_COLLAB_SECURITY_SETTINGS = 'EXTERNAL_COLLAB_SECURITY_SETTINGS' + FAILED_LOGIN = 'FAILED_LOGIN' + FILE_MARKED_MALICIOUS = 'FILE_MARKED_MALICIOUS' + FILE_WATERMARKED_DOWNLOAD = 'FILE_WATERMARKED_DOWNLOAD' + GROUP_ADD_ITEM = 'GROUP_ADD_ITEM' + GROUP_ADD_USER = 'GROUP_ADD_USER' + GROUP_CREATION = 'GROUP_CREATION' + GROUP_DELETION = 'GROUP_DELETION' + GROUP_EDITED = 'GROUP_EDITED' + GROUP_REMOVE_ITEM = 'GROUP_REMOVE_ITEM' + GROUP_REMOVE_USER = 'GROUP_REMOVE_USER' + ITEM_EMAIL_SEND = 'ITEM_EMAIL_SEND' + ITEM_MODIFY = 'ITEM_MODIFY' + ITEM_OPEN = 'ITEM_OPEN' + ITEM_SHARED_UPDATE = 'ITEM_SHARED_UPDATE' + ITEM_SYNC = 'ITEM_SYNC' + ITEM_UNSYNC = 'ITEM_UNSYNC' + LEGAL_HOLD_ASSIGNMENT_CREATE = 'LEGAL_HOLD_ASSIGNMENT_CREATE' + LEGAL_HOLD_ASSIGNMENT_DELETE = 'LEGAL_HOLD_ASSIGNMENT_DELETE' + LEGAL_HOLD_POLICY_CREATE = 'LEGAL_HOLD_POLICY_CREATE' + LEGAL_HOLD_POLICY_DELETE = 'LEGAL_HOLD_POLICY_DELETE' + LEGAL_HOLD_POLICY_UPDATE = 'LEGAL_HOLD_POLICY_UPDATE' + LOCK = 'LOCK' + LOGIN = 'LOGIN' + METADATA_INSTANCE_CREATE = 'METADATA_INSTANCE_CREATE' + METADATA_INSTANCE_DELETE = 'METADATA_INSTANCE_DELETE' + METADATA_INSTANCE_UPDATE = 'METADATA_INSTANCE_UPDATE' + METADATA_TEMPLATE_CREATE = 'METADATA_TEMPLATE_CREATE' + METADATA_TEMPLATE_DELETE = 'METADATA_TEMPLATE_DELETE' + METADATA_TEMPLATE_UPDATE = 'METADATA_TEMPLATE_UPDATE' + MOVE = 'MOVE' + NEW_USER = 'NEW_USER' + OAUTH2_ACCESS_TOKEN_REVOKE = 'OAUTH2_ACCESS_TOKEN_REVOKE' + PREVIEW = 'PREVIEW' + REMOVE_DEVICE_ASSOCIATION = 'REMOVE_DEVICE_ASSOCIATION' + REMOVE_LOGIN_ACTIVITY_DEVICE = 'REMOVE_LOGIN_ACTIVITY_DEVICE' + RENAME = 'RENAME' + RETENTION_POLICY_ASSIGNMENT_ADD = 'RETENTION_POLICY_ASSIGNMENT_ADD' + SHARE = 'SHARE' + SHARED_LINK_SEND = 'SHARED_LINK_SEND' + SHARE_EXPIRATION = 'SHARE_EXPIRATION' + SHIELD_ALERT = 'SHIELD_ALERT' + SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED = 'SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED' + SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION = ( + 'SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION' + ) + SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED = 'SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED' + SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION = ( + 'SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION' + ) + SHIELD_JUSTIFICATION_APPROVAL = 'SHIELD_JUSTIFICATION_APPROVAL' + SHIELD_SHARED_LINK_ACCESS_BLOCKED = 'SHIELD_SHARED_LINK_ACCESS_BLOCKED' + SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_CREATE = ( + 'SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_CREATE' + ) + SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_UPDATE = ( + 'SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_UPDATE' + ) + SIGN_DOCUMENT_ASSIGNED = 'SIGN_DOCUMENT_ASSIGNED' + SIGN_DOCUMENT_CANCELLED = 'SIGN_DOCUMENT_CANCELLED' + SIGN_DOCUMENT_COMPLETED = 'SIGN_DOCUMENT_COMPLETED' + SIGN_DOCUMENT_CONVERTED = 'SIGN_DOCUMENT_CONVERTED' + SIGN_DOCUMENT_CREATED = 'SIGN_DOCUMENT_CREATED' + SIGN_DOCUMENT_DECLINED = 'SIGN_DOCUMENT_DECLINED' + SIGN_DOCUMENT_EXPIRED = 'SIGN_DOCUMENT_EXPIRED' + SIGN_DOCUMENT_SIGNED = 'SIGN_DOCUMENT_SIGNED' + SIGN_DOCUMENT_VIEWED_BY_SIGNED = 'SIGN_DOCUMENT_VIEWED_BY_SIGNED' + SIGNER_DOWNLOADED = 'SIGNER_DOWNLOADED' + SIGNER_FORWARDED = 'SIGNER_FORWARDED' + STORAGE_EXPIRATION = 'STORAGE_EXPIRATION' + TASK_ASSIGNMENT_CREATE = 'TASK_ASSIGNMENT_CREATE' + TASK_ASSIGNMENT_DELETE = 'TASK_ASSIGNMENT_DELETE' + TASK_ASSIGNMENT_UPDATE = 'TASK_ASSIGNMENT_UPDATE' + TASK_CREATE = 'TASK_CREATE' + TASK_UPDATE = 'TASK_UPDATE' + TERMS_OF_SERVICE_ACCEPT = 'TERMS_OF_SERVICE_ACCEPT' + TERMS_OF_SERVICE_REJECT = 'TERMS_OF_SERVICE_REJECT' + UNDELETE = 'UNDELETE' + UNLOCK = 'UNLOCK' + UNSHARE = 'UNSHARE' + UPDATE_COLLABORATION_EXPIRATION = 'UPDATE_COLLABORATION_EXPIRATION' + UPDATE_SHARE_EXPIRATION = 'UPDATE_SHARE_EXPIRATION' + UPLOAD = 'UPLOAD' + USER_AUTHENTICATE_OAUTH2_ACCESS_TOKEN_CREATE = ( + 'USER_AUTHENTICATE_OAUTH2_ACCESS_TOKEN_CREATE' + ) + WATERMARK_LABEL_CREATE = 'WATERMARK_LABEL_CREATE' + WATERMARK_LABEL_DELETE = 'WATERMARK_LABEL_DELETE' + + +class GetEventStreamQueryParams: + def __init__( + self, + *, + stream_type: Optional[GetEventStreamQueryParamsStreamTypeField] = None, + stream_position: Optional[str] = None, + limit: Optional[int] = None, + event_type: Optional[List[GetEventStreamQueryParamsEventTypeField]] = None, + created_after: Optional[DateTime] = None, + created_before: Optional[DateTime] = None + ): + """ + :param stream_type: Defines the type of events that are returned + + * `all` returns everything for a user and is the default + * `changes` returns events that may cause file tree changes + such as file updates or collaborations. + * `sync` is similar to `changes` but only applies to synced folders + * `admin_logs` returns all events for an entire enterprise and + requires the user making the API call to have admin permissions. This + stream type is for programmatically pulling from a 1 year history of + events across all users within the enterprise and within a + `created_after` and `created_before` time frame. The complete history + of events will be returned in chronological order based on the event + time, but latency will be much higher than `admin_logs_streaming`. + * `admin_logs_streaming` returns all events for an entire enterprise and + requires the user making the API call to have admin permissions. This + stream type is for polling for recent events across all users within + the enterprise. Latency will be much lower than `admin_logs`, but + events will not be returned in chronological order and may + contain duplicates., defaults to None + :type stream_type: Optional[GetEventStreamQueryParamsStreamTypeField], optional + :param stream_position: The location in the event stream to start receiving events from. + + * `now` will return an empty list events and + the latest stream position for initialization. + * `0` or `null` will return all events., defaults to None + :type stream_position: Optional[str], optional + :param limit: Limits the number of events returned. + + Note: Sometimes, the events less than the limit requested can be returned + even when there may be more events remaining. This is primarily done in + the case where a number of events have already been retrieved and these + retrieved events are returned rather than delaying for an unknown amount + of time to see if there are any more results., defaults to None + :type limit: Optional[int], optional + :param event_type: A comma-separated list of events to filter by. This can only be used when + requesting the events with a `stream_type` of `admin_logs` or + `adming_logs_streaming`. For any other `stream_type` this value will be + ignored., defaults to None + :type event_type: Optional[List[GetEventStreamQueryParamsEventTypeField]], optional + :param created_after: The lower bound date and time to return events for. This can only be used + when requesting the events with a `stream_type` of `admin_logs`. For any + other `stream_type` this value will be ignored., defaults to None + :type created_after: Optional[DateTime], optional + :param created_before: The upper bound date and time to return events for. This can only be used + when requesting the events with a `stream_type` of `admin_logs`. For any + other `stream_type` this value will be ignored., defaults to None + :type created_before: Optional[DateTime], optional + """ + self.stream_type = stream_type + self.stream_position = stream_position + self.limit = limit + self.event_type = event_type + self.created_after = created_after + self.created_before = created_before + + +class GetEventStreamHeaders: + def __init__(self, *, extra_headers: Optional[Dict[str, Optional[str]]] = None): + """ + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + self.extra_headers = extra_headers + + +class EventsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_events_with_long_polling( + self, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RealtimeServers: + """ + Returns a list of real-time servers that can be used for long-polling updates + + to the [event stream](#get-events). + + + Long polling is the concept where a HTTP request is kept open until the + + + server sends a response, then repeating the process over and over to receive + + + updated responses. + + + Long polling the event stream can only be used for user events, not for + + + enterprise events. + + + To use long polling, first use this endpoint to retrieve a list of long poll + + + URLs. Next, make a long poll request to any of the provided URLs. + + + When an event occurs in monitored account a response with the value + + + `new_change` will be sent. The response contains no other details as + + + it only serves as a prompt to take further action such as sending a + + + request to the [events endpoint](#get-events) with the last known + + + `stream_position`. + + + After the server sends this response it closes the connection. You must now + + + repeat the long poll process to begin listening for events again. + + + If no events occur for a while and the connection times out you will + + + receive a response with the value `reconnect`. When you receive this response + + + you’ll make another call to this endpoint to restart the process. + + + If you receive no events in `retry_timeout` seconds then you will need to + + + make another request to the real-time server (one of the URLs in the response + + + for this endpoint). This might be necessary due to network errors. + + + Finally, if you receive a `max_retries` error when making a request to the + + + real-time server, you should start over by making a call to this endpoint + + + first. + + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/events']), + method='OPTIONS', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RealtimeServers) + + def get_events( + self, + *, + stream_type: Optional[GetEventsStreamType] = None, + stream_position: Optional[str] = None, + limit: Optional[int] = None, + event_type: Optional[List[GetEventsEventType]] = None, + created_after: Optional[DateTime] = None, + created_before: Optional[DateTime] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Events: + """ + Returns up to a year of past events for a given user + + or for the entire enterprise. + + + By default this returns events for the authenticated user. To retrieve events + + + for the entire enterprise, set the `stream_type` to `admin_logs_streaming` + + + for live monitoring of new events, or `admin_logs` for querying across + + + historical events. The user making the API call will + + + need to have admin privileges, and the application will need to have the + + + scope `manage enterprise properties` checked. + + :param stream_type: Defines the type of events that are returned + + * `all` returns everything for a user and is the default + * `changes` returns events that may cause file tree changes + such as file updates or collaborations. + * `sync` is similar to `changes` but only applies to synced folders + * `admin_logs` returns all events for an entire enterprise and + requires the user making the API call to have admin permissions. This + stream type is for programmatically pulling from a 1 year history of + events across all users within the enterprise and within a + `created_after` and `created_before` time frame. The complete history + of events will be returned in chronological order based on the event + time, but latency will be much higher than `admin_logs_streaming`. + * `admin_logs_streaming` returns all events for an entire enterprise and + requires the user making the API call to have admin permissions. This + stream type is for polling for recent events across all users within + the enterprise. Latency will be much lower than `admin_logs`, but + events will not be returned in chronological order and may + contain duplicates., defaults to None + :type stream_type: Optional[GetEventsStreamType], optional + :param stream_position: The location in the event stream to start receiving events from. + + * `now` will return an empty list events and + the latest stream position for initialization. + * `0` or `null` will return all events., defaults to None + :type stream_position: Optional[str], optional + :param limit: Limits the number of events returned. + + Note: Sometimes, the events less than the limit requested can be returned + even when there may be more events remaining. This is primarily done in + the case where a number of events have already been retrieved and these + retrieved events are returned rather than delaying for an unknown amount + of time to see if there are any more results., defaults to None + :type limit: Optional[int], optional + :param event_type: A comma-separated list of events to filter by. This can only be used when + requesting the events with a `stream_type` of `admin_logs` or + `adming_logs_streaming`. For any other `stream_type` this value will be + ignored., defaults to None + :type event_type: Optional[List[GetEventsEventType]], optional + :param created_after: The lower bound date and time to return events for. This can only be used + when requesting the events with a `stream_type` of `admin_logs`. For any + other `stream_type` this value will be ignored., defaults to None + :type created_after: Optional[DateTime], optional + :param created_before: The upper bound date and time to return events for. This can only be used + when requesting the events with a `stream_type` of `admin_logs`. For any + other `stream_type` this value will be ignored., defaults to None + :type created_before: Optional[DateTime], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'stream_type': to_string(stream_type), + 'stream_position': to_string(stream_position), + 'limit': to_string(limit), + 'event_type': to_string(event_type), + 'created_after': to_string(created_after), + 'created_before': to_string(created_before), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/events']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Events) + + def get_event_stream( + self, + *, + query_params: GetEventStreamQueryParams = None, + headers: GetEventStreamHeaders = None + ) -> EventStream: + """ + Get an event stream for the Box API + :param query_params: Query parameters of getEvents method, defaults to None + :type query_params: GetEventStreamQueryParams, optional + :param headers: Headers of getEvents method, defaults to None + :type headers: GetEventStreamHeaders, optional + """ + if query_params is None: + query_params = GetEventStreamQueryParams() + if headers is None: + headers = GetEventStreamHeaders() + return EventStream( + events_manager=self, query_params=query_params, headers_input=headers + ) diff --git a/box_sdk_gen/managers/external_users.py b/box_sdk_gen/managers/external_users.py new file mode 100644 index 000000000..cf69a6a3c --- /dev/null +++ b/box_sdk_gen/managers/external_users.py @@ -0,0 +1,106 @@ +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.schemas.v2025_r0.user_reference_v2025_r0 import UserReferenceV2025R0 + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.external_users_submit_delete_job_response_v2025_r0 import ( + ExternalUsersSubmitDeleteJobResponseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.external_users_submit_delete_job_request_v2025_r0 import ( + ExternalUsersSubmitDeleteJobRequestV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class ExternalUsersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def submit_job_to_delete_external_users_v2025_r0( + self, + external_users: List[UserReferenceV2025R0], + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ExternalUsersSubmitDeleteJobResponseV2025R0: + """ + Delete external users from current user enterprise. This will remove each + + external user from all invited collaborations within the current enterprise. + + :param external_users: List of external users to delete. + :type external_users: List[UserReferenceV2025R0] + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'external_users': external_users} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/external_users/submit_delete_job', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ExternalUsersSubmitDeleteJobResponseV2025R0) diff --git a/box_sdk_gen/managers/file_classifications.py b/box_sdk_gen/managers/file_classifications.py new file mode 100644 index 000000000..c38a2bbff --- /dev/null +++ b/box_sdk_gen/managers/file_classifications.py @@ -0,0 +1,313 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from typing import List + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.classification import Classification + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class UpdateClassificationOnFileRequestBodyOpField(str, Enum): + REPLACE = 'replace' + + +class UpdateClassificationOnFileRequestBodyPathField(str, Enum): + _BOX__SECURITY__CLASSIFICATION__KEY = '/Box__Security__Classification__Key' + + +class UpdateClassificationOnFileRequestBody(BaseObject): + def __init__( + self, + value: str, + *, + op: UpdateClassificationOnFileRequestBodyOpField = UpdateClassificationOnFileRequestBodyOpField.REPLACE, + path: UpdateClassificationOnFileRequestBodyPathField = UpdateClassificationOnFileRequestBodyPathField._BOX__SECURITY__CLASSIFICATION__KEY, + **kwargs + ): + """ + :param value: The name of the classification to apply to this file. + + To list the available classifications in an enterprise, + use the classification API to retrieve the + [classification template](e://get_metadata_templates_enterprise_securityClassification-6VMVochwUWo_schema) + which lists all available classification keys. + :type value: str + :param op: The value will always be `replace`., defaults to UpdateClassificationOnFileRequestBodyOpField.REPLACE + :type op: UpdateClassificationOnFileRequestBodyOpField, optional + :param path: Defines classifications + available in the enterprise., defaults to UpdateClassificationOnFileRequestBodyPathField._BOX__SECURITY__CLASSIFICATION__KEY + :type path: UpdateClassificationOnFileRequestBodyPathField, optional + """ + super().__init__(**kwargs) + self.value = value + self.op = op + self.path = path + + +class FileClassificationsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_classification_on_file( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Classification: + """ + Retrieves the classification metadata instance that + + has been applied to a file. + + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/files/:id//enterprise_12345/securityClassification-6VMVochwUWo`. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Classification) + + def add_classification_to_file( + self, + file_id: str, + *, + box_security_classification_key: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Classification: + """ + Adds a classification to a file by specifying the label of the + + classification to add. + + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/files/:id//enterprise_12345/securityClassification-6VMVochwUWo`. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param box_security_classification_key: The name of the classification to apply to this file. + + To list the available classifications in an enterprise, + use the classification API to retrieve the + [classification template](e://get_metadata_templates_enterprise_securityClassification-6VMVochwUWo_schema) + which lists all available classification keys., defaults to None + :type box_security_classification_key: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'Box__Security__Classification__Key': box_security_classification_key + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Classification) + + def update_classification_on_file( + self, + file_id: str, + request_body: List[UpdateClassificationOnFileRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Classification: + """ + Updates a classification on a file. + + The classification can only be updated if a classification has already been + + + applied to the file before. When editing classifications, only values are + + + defined for the enterprise will be accepted. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param request_body: Request body of updateClassificationOnFile method + :type request_body: List[UpdateClassificationOnFileRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json-patch+json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Classification) + + def delete_classification_from_file( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes any classifications from a file. + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/files/:id//enterprise_12345/securityClassification-6VMVochwUWo`. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/file_metadata.py b/box_sdk_gen/managers/file_metadata.py new file mode 100644 index 000000000..8dc0577f2 --- /dev/null +++ b/box_sdk_gen/managers/file_metadata.py @@ -0,0 +1,420 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from typing import List + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.metadatas import Metadatas + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.metadata_full import MetadataFull + +from box_sdk_gen.schemas.metadata_error import MetadataError + +from box_sdk_gen.schemas.metadata_instance_value import MetadataInstanceValue + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetFileMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class CreateFileMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class UpdateFileMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class UpdateFileMetadataByIdRequestBodyOpField(str, Enum): + ADD = 'add' + REPLACE = 'replace' + REMOVE = 'remove' + TEST = 'test' + MOVE = 'move' + COPY = 'copy' + + +class UpdateFileMetadataByIdRequestBody(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'from_': 'from', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'from': 'from_', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + op: Optional[UpdateFileMetadataByIdRequestBodyOpField] = None, + path: Optional[str] = None, + value: Optional[MetadataInstanceValue] = None, + from_: Optional[str] = None, + **kwargs + ): + """ + :param op: The type of change to perform on the template. Some + of these are hazardous as they will change existing templates., defaults to None + :type op: Optional[UpdateFileMetadataByIdRequestBodyOpField], optional + :param path: The location in the metadata JSON object + to apply the changes to, in the format of a + [JSON-Pointer](https://tools.ietf.org/html/rfc6901). + + The path must always be prefixed with a `/` to represent the root + of the template. The characters `~` and `/` are reserved + characters and must be escaped in the key., defaults to None + :type path: Optional[str], optional + :param from_: The location in the metadata JSON object to move or copy a value + from. Required for `move` or `copy` operations and must be in the + format of a [JSON-Pointer](https://tools.ietf.org/html/rfc6901)., defaults to None + :type from_: Optional[str], optional + """ + super().__init__(**kwargs) + self.op = op + self.path = path + self.value = value + self.from_ = from_ + + +class DeleteFileMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class FileMetadataManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_metadata( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Metadatas: + """ + Retrieves all metadata for a given file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Metadatas) + + def get_file_metadata_by_id( + self, + file_id: str, + scope: GetFileMetadataByIdScope, + template_key: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataFull: + """ + Retrieves the instance of a metadata template that has been applied to a + + file. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: GetFileMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataFull) + + def create_file_metadata_by_id( + self, + file_id: str, + scope: CreateFileMetadataByIdScope, + template_key: str, + request_body: Dict, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataFull: + """ + Applies an instance of a metadata template to a file. + + In most cases only values that are present in the metadata template + + + will be accepted, except for the `global.properties` template which accepts + + + any key-value pair. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: CreateFileMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param request_body: Request body of createFileMetadataById method + :type request_body: Dict + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataFull) + + def update_file_metadata_by_id( + self, + file_id: str, + scope: UpdateFileMetadataByIdScope, + template_key: str, + request_body: List[UpdateFileMetadataByIdRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataFull: + """ + Updates a piece of metadata on a file. + + The metadata instance can only be updated if the template has already been + + + applied to the file before. When editing metadata, only values that match + + + the metadata template schema will be accepted. + + + The update is applied atomically. If any errors occur during the + + + application of the operations, the metadata instance will not be changed. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: UpdateFileMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param request_body: Request body of updateFileMetadataById method + :type request_body: List[UpdateFileMetadataByIdRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json-patch+json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataFull) + + def delete_file_metadata_by_id( + self, + file_id: str, + scope: DeleteFileMetadataByIdScope, + template_key: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a piece of file metadata. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: DeleteFileMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/file_requests.py b/box_sdk_gen/managers/file_requests.py new file mode 100644 index 000000000..594365858 --- /dev/null +++ b/box_sdk_gen/managers/file_requests.py @@ -0,0 +1,398 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.file_request import FileRequest + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.file_request_update_request import FileRequestUpdateRequest + +from box_sdk_gen.schemas.file_request_copy_request import FileRequestCopyRequest + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class UpdateFileRequestByIdStatus(str, Enum): + ACTIVE = 'active' + INACTIVE = 'inactive' + + +class CreateFileRequestCopyFolderTypeField(str, Enum): + FOLDER = 'folder' + + +class CreateFileRequestCopyFolder(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + type: Optional[CreateFileRequestCopyFolderTypeField] = None, + **kwargs + ): + """ + :param id: The ID of the folder to associate the new + file request to. + :type id: str + :param type: The value will always be `folder`., defaults to None + :type type: Optional[CreateFileRequestCopyFolderTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateFileRequestCopyStatus(str, Enum): + ACTIVE = 'active' + INACTIVE = 'inactive' + + +class FileRequestsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_request_by_id( + self, + file_request_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileRequest: + """ + Retrieves the information about a file request. + :param file_request_id: The unique identifier that represent a file request. + + The ID for any file request can be determined + by visiting a file request builder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/filerequest/123` + the `file_request_id` is `123`. + Example: "123" + :type file_request_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_requests/', + to_string(file_request_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileRequest) + + def update_file_request_by_id( + self, + file_request_id: str, + *, + title: Optional[str] = None, + description: Optional[str] = None, + status: Optional[UpdateFileRequestByIdStatus] = None, + is_email_required: Optional[bool] = None, + is_description_required: Optional[bool] = None, + expires_at: Optional[DateTime] = None, + if_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileRequest: + """ + Updates a file request. This can be used to activate or + + deactivate a file request. + + :param file_request_id: The unique identifier that represent a file request. + + The ID for any file request can be determined + by visiting a file request builder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/filerequest/123` + the `file_request_id` is `123`. + Example: "123" + :type file_request_id: str + :param title: An optional new title for the file request. This can be + used to change the title of the file request. + + This will default to the value on the existing file request., defaults to None + :type title: Optional[str], optional + :param description: An optional new description for the file request. This can be + used to change the description of the file request. + + This will default to the value on the existing file request., defaults to None + :type description: Optional[str], optional + :param status: An optional new status of the file request. + + When the status is set to `inactive`, the file request + will no longer accept new submissions, and any visitor + to the file request URL will receive a `HTTP 404` status + code. + + This will default to the value on the existing file request., defaults to None + :type status: Optional[UpdateFileRequestByIdStatus], optional + :param is_email_required: Whether a file request submitter is required to provide + their email address. + + When this setting is set to true, the Box UI will show + an email field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_email_required: Optional[bool], optional + :param is_description_required: Whether a file request submitter is required to provide + a description of the files they are submitting. + + When this setting is set to true, the Box UI will show + a description field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_description_required: Optional[bool], optional + :param expires_at: The date after which a file request will no longer accept new + submissions. + + After this date, the `status` will automatically be set to + `inactive`. + + This will default to the value on the existing file request., defaults to None + :type expires_at: Optional[DateTime], optional + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'title': title, + 'description': description, + 'status': status, + 'is_email_required': is_email_required, + 'is_description_required': is_description_required, + 'expires_at': expires_at, + } + headers_map: Dict[str, str] = prepare_params( + {'if-match': to_string(if_match), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_requests/', + to_string(file_request_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileRequest) + + def delete_file_request_by_id( + self, + file_request_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a file request permanently. + :param file_request_id: The unique identifier that represent a file request. + + The ID for any file request can be determined + by visiting a file request builder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/filerequest/123` + the `file_request_id` is `123`. + Example: "123" + :type file_request_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_requests/', + to_string(file_request_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def create_file_request_copy( + self, + file_request_id: str, + folder: CreateFileRequestCopyFolder, + *, + title: Optional[str] = None, + description: Optional[str] = None, + status: Optional[CreateFileRequestCopyStatus] = None, + is_email_required: Optional[bool] = None, + is_description_required: Optional[bool] = None, + expires_at: Optional[DateTime] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileRequest: + """ + Copies an existing file request that is already present on one folder, + + and applies it to another folder. + + :param file_request_id: The unique identifier that represent a file request. + + The ID for any file request can be determined + by visiting a file request builder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/filerequest/123` + the `file_request_id` is `123`. + Example: "123" + :type file_request_id: str + :param folder: The folder to associate the new file request to. + :type folder: CreateFileRequestCopyFolder + :param title: An optional new title for the file request. This can be + used to change the title of the file request. + + This will default to the value on the existing file request., defaults to None + :type title: Optional[str], optional + :param description: An optional new description for the file request. This can be + used to change the description of the file request. + + This will default to the value on the existing file request., defaults to None + :type description: Optional[str], optional + :param status: An optional new status of the file request. + + When the status is set to `inactive`, the file request + will no longer accept new submissions, and any visitor + to the file request URL will receive a `HTTP 404` status + code. + + This will default to the value on the existing file request., defaults to None + :type status: Optional[CreateFileRequestCopyStatus], optional + :param is_email_required: Whether a file request submitter is required to provide + their email address. + + When this setting is set to true, the Box UI will show + an email field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_email_required: Optional[bool], optional + :param is_description_required: Whether a file request submitter is required to provide + a description of the files they are submitting. + + When this setting is set to true, the Box UI will show + a description field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_description_required: Optional[bool], optional + :param expires_at: The date after which a file request will no longer accept new + submissions. + + After this date, the `status` will automatically be set to + `inactive`. + + This will default to the value on the existing file request., defaults to None + :type expires_at: Optional[DateTime], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'folder': folder, + 'title': title, + 'description': description, + 'status': status, + 'is_email_required': is_email_required, + 'is_description_required': is_description_required, + 'expires_at': expires_at, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_requests/', + to_string(file_request_id), + '/copy', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileRequest) diff --git a/box_sdk_gen/managers/file_version_legal_holds.py b/box_sdk_gen/managers/file_version_legal_holds.py new file mode 100644 index 000000000..2dc2451de --- /dev/null +++ b/box_sdk_gen/managers/file_version_legal_holds.py @@ -0,0 +1,179 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.file_version_legal_hold import FileVersionLegalHold + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.file_version_legal_holds import FileVersionLegalHolds + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class FileVersionLegalHoldsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_version_legal_hold_by_id( + self, + file_version_legal_hold_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersionLegalHold: + """ + Retrieves information about the legal hold policies + + assigned to a file version. + + :param file_version_legal_hold_id: The ID of the file version legal hold. + Example: "2348213" + :type file_version_legal_hold_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_version_legal_holds/', + to_string(file_version_legal_hold_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersionLegalHold) + + def get_file_version_legal_holds( + self, + policy_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersionLegalHolds: + """ + Get a list of file versions on legal hold for a legal hold + + assignment. + + + Due to ongoing re-architecture efforts this API might not return all file + + + versions for this policy ID. + + + Instead, this API will only return file versions held in the legacy + + + architecture. Two new endpoints will available to request any file versions + + + held in the new architecture. + + + For file versions held in the new architecture, the `GET + + + /legal_hold_policy_assignments/:id/file_versions_on_hold` API can be used to + + + return all past file versions available for this policy assignment, and the + + + `GET /legal_hold_policy_assignments/:id/files_on_hold` API can be used to + + + return any current (latest) versions of a file under legal hold. + + + The `GET /legal_hold_policy_assignments?policy_id={id}` API can be used to + + + find a list of policy assignments for a given policy ID. + + + Once the re-architecture is completed this API will be deprecated. + + :param policy_id: The ID of the legal hold policy to get the file version legal + holds for. + :type policy_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'policy_id': to_string(policy_id), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_version_legal_holds', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersionLegalHolds) diff --git a/box_sdk_gen/managers/file_version_retentions.py b/box_sdk_gen/managers/file_version_retentions.py new file mode 100644 index 000000000..8c2ddf75c --- /dev/null +++ b/box_sdk_gen/managers/file_version_retentions.py @@ -0,0 +1,187 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.file_version_retentions import FileVersionRetentions + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.file_version_retention import FileVersionRetention + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetFileVersionRetentionsDispositionAction(str, Enum): + PERMANENTLY_DELETE = 'permanently_delete' + REMOVE_RETENTION = 'remove_retention' + + +class FileVersionRetentionsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_version_retentions( + self, + *, + file_id: Optional[str] = None, + file_version_id: Optional[str] = None, + policy_id: Optional[str] = None, + disposition_action: Optional[GetFileVersionRetentionsDispositionAction] = None, + disposition_before: Optional[str] = None, + disposition_after: Optional[str] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersionRetentions: + """ + Retrieves all file version retentions for the given enterprise. + + **Note**: + + + File retention API is now **deprecated**. + + + To get information about files and file versions under retention, + + + see [files under retention](e://get-retention-policy-assignments-id-files-under-retention) or [file versions under retention](e://get-retention-policy-assignments-id-file-versions-under-retention) endpoints. + + :param file_id: Filters results by files with this ID., defaults to None + :type file_id: Optional[str], optional + :param file_version_id: Filters results by file versions with this ID., defaults to None + :type file_version_id: Optional[str], optional + :param policy_id: Filters results by the retention policy with this ID., defaults to None + :type policy_id: Optional[str], optional + :param disposition_action: Filters results by the retention policy with this disposition + action., defaults to None + :type disposition_action: Optional[GetFileVersionRetentionsDispositionAction], optional + :param disposition_before: Filters results by files that will have their disposition + come into effect before this date., defaults to None + :type disposition_before: Optional[str], optional + :param disposition_after: Filters results by files that will have their disposition + come into effect after this date., defaults to None + :type disposition_after: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'file_id': to_string(file_id), + 'file_version_id': to_string(file_version_id), + 'policy_id': to_string(policy_id), + 'disposition_action': to_string(disposition_action), + 'disposition_before': to_string(disposition_before), + 'disposition_after': to_string(disposition_after), + 'limit': to_string(limit), + 'marker': to_string(marker), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_version_retentions', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersionRetentions) + + def get_file_version_retention_by_id( + self, + file_version_retention_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersionRetention: + """ + Returns information about a file version retention. + + **Note**: + + + File retention API is now **deprecated**. + + + To get information about files and file versions under retention, + + + see [files under retention](e://get-retention-policy-assignments-id-files-under-retention) or [file versions under retention](e://get-retention-policy-assignments-id-file-versions-under-retention) endpoints. + + :param file_version_retention_id: The ID of the file version retention. + Example: "3424234" + :type file_version_retention_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/file_version_retentions/', + to_string(file_version_retention_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersionRetention) diff --git a/box_sdk_gen/managers/file_versions.py b/box_sdk_gen/managers/file_versions.py new file mode 100644 index 000000000..238a25ae4 --- /dev/null +++ b/box_sdk_gen/managers/file_versions.py @@ -0,0 +1,423 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.file_versions import FileVersions + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.file_version_full import FileVersionFull + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class PromoteFileVersionType(str, Enum): + FILE_VERSION = 'file_version' + + +class FileVersionsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_versions( + self, + file_id: str, + *, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersions: + """ + Retrieve a list of the past versions for a file. + + Versions are only tracked by Box users with premium accounts. To fetch the ID + + + of the current version of a file, use the `GET /file/:id` API. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'limit': to_string(limit), + 'offset': to_string(offset), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/versions', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersions) + + def get_file_version_by_id( + self, + file_id: str, + file_version_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersionFull: + """ + Retrieve a specific version of a file. + + Versions are only tracked for Box users with premium accounts. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param file_version_id: The ID of the file version. + Example: "1234" + :type file_version_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/versions/', + to_string(file_version_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersionFull) + + def delete_file_version_by_id( + self, + file_id: str, + file_version_id: str, + *, + if_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Move a file version to the trash. + + Versions are only tracked for Box users with premium accounts. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param file_version_id: The ID of the file version. + Example: "1234" + :type file_version_id: str + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'if-match': to_string(if_match), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/versions/', + to_string(file_version_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def update_file_version_by_id( + self, + file_id: str, + file_version_id: str, + *, + trashed_at: Union[Optional[str], NullValue] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersionFull: + """ + Restores a specific version of a file after it was deleted. + + Don't use this endpoint to restore Box Notes, + + + as it works with file formats such as PDF, DOC, + + + PPTX or similar. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param file_version_id: The ID of the file version. + Example: "1234" + :type file_version_id: str + :param trashed_at: Set this to `null` to clear + the date and restore the file., defaults to None + :type trashed_at: Union[Optional[str], NullValue], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'trashed_at': trashed_at} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/versions/', + to_string(file_version_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersionFull) + + def promote_file_version( + self, + file_id: str, + *, + id: Optional[str] = None, + type: Optional[PromoteFileVersionType] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileVersionFull: + """ + Promote a specific version of a file. + + If previous versions exist, this method can be used to + + + promote one of the older versions to the top of the version history. + + + This creates a new copy of the old version and puts it at the + + + top of the versions history. The file will have the exact same contents + + + as the older version, with the same hash digest, `etag`, and + + + name as the original. + + + Other properties such as comments do not get updated to their + + + former values. + + + Don't use this endpoint to restore Box Notes, + + + as it works with file formats such as PDF, DOC, + + + PPTX or similar. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param id: The file version ID., defaults to None + :type id: Optional[str], optional + :param type: The type to promote., defaults to None + :type type: Optional[PromoteFileVersionType], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'id': id, 'type': type} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/versions/current', + ] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileVersionFull) diff --git a/box_sdk_gen/managers/file_watermarks.py b/box_sdk_gen/managers/file_watermarks.py new file mode 100644 index 000000000..c82eeb42d --- /dev/null +++ b/box_sdk_gen/managers/file_watermarks.py @@ -0,0 +1,199 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.watermark import Watermark + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class UpdateFileWatermarkWatermarkImprintField(str, Enum): + DEFAULT = 'default' + + +class UpdateFileWatermarkWatermark(BaseObject): + def __init__( + self, + *, + imprint: UpdateFileWatermarkWatermarkImprintField = UpdateFileWatermarkWatermarkImprintField.DEFAULT, + **kwargs + ): + """ + :param imprint: The type of watermark to apply. + + Currently only supports one option., defaults to UpdateFileWatermarkWatermarkImprintField.DEFAULT + :type imprint: UpdateFileWatermarkWatermarkImprintField, optional + """ + super().__init__(**kwargs) + self.imprint = imprint + + +class FileWatermarksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_watermark( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Watermark: + """ + Retrieve the watermark for a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/watermark', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Watermark) + + def update_file_watermark( + self, + file_id: str, + watermark: UpdateFileWatermarkWatermark, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Watermark: + """ + Applies or update a watermark on a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param watermark: The watermark to imprint on the file. + :type watermark: UpdateFileWatermarkWatermark + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'watermark': watermark} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/watermark', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Watermark) + + def delete_file_watermark( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes the watermark from a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/watermark', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/files.py b/box_sdk_gen/managers/files.py new file mode 100644 index 000000000..b153e2a61 --- /dev/null +++ b/box_sdk_gen/managers/files.py @@ -0,0 +1,786 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from enum import Enum + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class UpdateFileByIdParent(BaseObject): + def __init__( + self, *, id: Optional[str] = None, user_id: Optional[str] = None, **kwargs + ): + """ + :param id: The ID of parent item., defaults to None + :type id: Optional[str], optional + :param user_id: The input for `user_id` is optional. Moving to non-root folder is not allowed when `user_id` is present. Parent folder id should be zero when `user_id` is provided., defaults to None + :type user_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.user_id = user_id + + +class UpdateFileByIdSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class UpdateFileByIdSharedLinkPermissionsField(BaseObject): + def __init__(self, *, can_download: Optional[bool] = None, **kwargs): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + + +class UpdateFileByIdSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateFileByIdSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[UpdateFileByIdSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the folder (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[UpdateFileByIdSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class UpdateFileByIdLockAccessField(str, Enum): + LOCK = 'lock' + + +class UpdateFileByIdLock(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateFileByIdLockAccessField] = None, + expires_at: Optional[DateTime] = None, + is_download_prevented: Optional[bool] = None, + **kwargs + ): + """ + :param access: The type of this object., defaults to None + :type access: Optional[UpdateFileByIdLockAccessField], optional + :param expires_at: Defines the time at which the lock expires., defaults to None + :type expires_at: Optional[DateTime], optional + :param is_download_prevented: Defines if the file can be downloaded while it is locked., defaults to None + :type is_download_prevented: Optional[bool], optional + """ + super().__init__(**kwargs) + self.access = access + self.expires_at = expires_at + self.is_download_prevented = is_download_prevented + + +class UpdateFileByIdPermissionsCanDownloadField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + + +class UpdateFileByIdPermissions(BaseObject): + def __init__( + self, + *, + can_download: Optional[UpdateFileByIdPermissionsCanDownloadField] = None, + **kwargs + ): + """ + :param can_download: Defines who is allowed to download this file. The possible + values are either `open` for everyone or `company` for + the other members of the user's enterprise. + + This setting overrides the download permissions that are + normally part of the `role` of a collaboration. When set to + `company`, this essentially removes the download option for + external users with `viewer` or `editor` a roles., defaults to None + :type can_download: Optional[UpdateFileByIdPermissionsCanDownloadField], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + + +class UpdateFileByIdCollections(BaseObject): + def __init__( + self, *, id: Optional[str] = None, type: Optional[str] = None, **kwargs + ): + """ + :param id: The unique identifier for this object., defaults to None + :type id: Optional[str], optional + :param type: The type for this object., defaults to None + :type type: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CopyFileParent(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of folder to copy the file to. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class GetFileThumbnailUrlExtension(str, Enum): + PNG = 'png' + JPG = 'jpg' + + +class GetFileThumbnailByIdExtension(str, Enum): + PNG = 'png' + JPG = 'jpg' + + +class FilesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_by_id( + self, + file_id: str, + *, + fields: Optional[List[str]] = None, + if_none_match: Optional[str] = None, + boxapi: Optional[str] = None, + x_rep_hints: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + """ + Retrieves the details about a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested. + + Additionally this field can be used to query any metadata + applied to the file by specifying the `metadata` field as well + as the scope and key of the template to retrieve, for example + `?fields=metadata.enterprise_12345.contractTemplate`., defaults to None + :type fields: Optional[List[str]], optional + :param if_none_match: Ensures an item is only returned if it has changed. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `304 Not Modified` if the item has not + changed since., defaults to None + :type if_none_match: Optional[str], optional + :param boxapi: The URL, and optional password, for the shared link of this item. + + This header can be used to access items that have not been + explicitly shared with a user. + + Use the format `shared_link=[link]` or if a password is required then + use `shared_link=[link]&shared_link_password=[password]`. + + This header can be used on the file or folder shared, as well as on any files + or folders nested within the item., defaults to None + :type boxapi: Optional[str], optional + :param x_rep_hints: A header required to request specific `representations` + of a file. Use this in combination with the `fields` query + parameter to request a specific file representation. + + The general format for these representations is + `X-Rep-Hints: [...]` where `[...]` is one or many + hints in the format `[fileType?query]`. + + For example, to request a `png` representation in `32x32` + as well as `64x64` pixel dimensions provide the following + hints. + + `x-rep-hints: [jpg?dimensions=32x32][jpg?dimensions=64x64]` + + Additionally, a `text` representation is available for all + document file types in Box using the `[extracted_text]` + representation. + + `x-rep-hints: [extracted_text]`., defaults to None + :type x_rep_hints: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + { + 'if-none-match': to_string(if_none_match), + 'boxapi': to_string(boxapi), + 'x-rep-hints': to_string(x_rep_hints), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) + + def update_file_by_id( + self, + file_id: str, + *, + name: Optional[str] = None, + description: Optional[str] = None, + parent: Optional[UpdateFileByIdParent] = None, + shared_link: Union[Optional[UpdateFileByIdSharedLink], NullValue] = None, + lock: Union[Optional[UpdateFileByIdLock], NullValue] = None, + disposition_at: Optional[DateTime] = None, + permissions: Optional[UpdateFileByIdPermissions] = None, + collections: Union[Optional[List[UpdateFileByIdCollections]], NullValue] = None, + tags: Optional[List[str]] = None, + fields: Optional[List[str]] = None, + if_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + """ + Updates a file. This can be used to rename or move a file, + + create a shared link, or lock a file. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param name: An optional different name for the file. This can be used to + rename the file. + + File names must be unique within their parent folder. The name check is case-insensitive, so a file + named `New File` cannot be created in a parent folder that already contains a folder named `new file`., defaults to None + :type name: Optional[str], optional + :param description: The description for a file. This can be seen in the right-hand sidebar panel + when viewing a file in the Box web app. Additionally, this index is used in + the search index of the file, allowing users to find the file by the content + in the description., defaults to None + :type description: Optional[str], optional + :param lock: Defines a lock on an item. This prevents the item from being + moved, renamed, or otherwise changed by anyone other than the user + who created the lock. + + Set this to `null` to remove the lock., defaults to None + :type lock: Union[Optional[UpdateFileByIdLock], NullValue], optional + :param disposition_at: The retention expiration timestamp for the given file. This + date cannot be shortened once set on a file., defaults to None + :type disposition_at: Optional[DateTime], optional + :param permissions: Defines who can download a file., defaults to None + :type permissions: Optional[UpdateFileByIdPermissions], optional + :param collections: An array of collections to make this file + a member of. Currently + we only support the `favorites` collection. + + To get the ID for a collection, use the + [List all collections][1] endpoint. + + Passing an empty array `[]` or `null` will remove + the file from all collections. + + [1]: e://get-collections, defaults to None + :type collections: Union[Optional[List[UpdateFileByIdCollections]], NullValue], optional + :param tags: The tags for this item. These tags are shown in + the Box web app and mobile apps next to an item. + + To add or remove a tag, retrieve the item's current tags, + modify them, and then update this field. + + There is a limit of 100 tags per item, and 10,000 + unique tags per enterprise., defaults to None + :type tags: Optional[List[str]], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'description': description, + 'parent': parent, + 'shared_link': shared_link, + 'lock': lock, + 'disposition_at': disposition_at, + 'permissions': permissions, + 'collections': collections, + 'tags': tags, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + {'if-match': to_string(if_match), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) + + def delete_file_by_id( + self, + file_id: str, + *, + if_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a file, either permanently or by moving it to + + the trash. + + + The enterprise settings determine whether the item will + + + be permanently deleted from Box or moved to the trash. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'if-match': to_string(if_match), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def copy_file( + self, + file_id: str, + parent: CopyFileParent, + *, + name: Optional[str] = None, + version: Optional[str] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + r""" + Creates a copy of a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param parent: The destination folder to copy the file to. + :type parent: CopyFileParent + :param name: An optional new name for the copied file. + + There are some restrictions to the file name. Names containing + non-printable ASCII characters, forward and backward slashes + (`/`, `\`), and protected names like `.` and `..` are + automatically sanitized by removing the non-allowed + characters., defaults to None + :type name: Optional[str], optional + :param version: An optional ID of the specific file version to copy., defaults to None + :type version: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name, 'version': version, 'parent': parent} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/copy', + ] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) + + def get_file_thumbnail_url( + self, + file_id: str, + extension: GetFileThumbnailUrlExtension, + *, + min_height: Optional[int] = None, + min_width: Optional[int] = None, + max_height: Optional[int] = None, + max_width: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> str: + """ + Retrieves a thumbnail, or smaller image representation, of a file. + + Sizes of `32x32`,`64x64`, `128x128`, and `256x256` can be returned in + + + the `.png` format and sizes of `32x32`, `160x160`, and `320x320` + + + can be returned in the `.jpg` format. + + + Thumbnails can be generated for the image and video file formats listed + + + [found on our community site][1]. + + + [1]: https://community.box.com/t5/Migrating-and-Previewing-Content/File-Types-and-Fonts-Supported-in-Box-Content-Preview/ta-p/327 + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extension: The file format for the thumbnail. + Example: "png" + :type extension: GetFileThumbnailUrlExtension + :param min_height: The minimum height of the thumbnail., defaults to None + :type min_height: Optional[int], optional + :param min_width: The minimum width of the thumbnail., defaults to None + :type min_width: Optional[int], optional + :param max_height: The maximum height of the thumbnail., defaults to None + :type max_height: Optional[int], optional + :param max_width: The maximum width of the thumbnail., defaults to None + :type max_width: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'min_height': to_string(min_height), + 'min_width': to_string(min_width), + 'max_height': to_string(max_height), + 'max_width': to_string(max_width), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/thumbnail.', + to_string(extension), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + follow_redirects=False, + ) + ) + if 'location' in response.headers: + return response.headers.get('location') + if 'Location' in response.headers: + return response.headers.get('Location') + raise BoxSDKError(message='No location header in response') + + def get_file_thumbnail_by_id( + self, + file_id: str, + extension: GetFileThumbnailByIdExtension, + *, + min_height: Optional[int] = None, + min_width: Optional[int] = None, + max_height: Optional[int] = None, + max_width: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Optional[ByteStream]: + """ + Retrieves a thumbnail, or smaller image representation, of a file. + + Sizes of `32x32`,`64x64`, `128x128`, and `256x256` can be returned in + + + the `.png` format and sizes of `32x32`, `160x160`, and `320x320` + + + can be returned in the `.jpg` format. + + + Thumbnails can be generated for the image and video file formats listed + + + [found on our community site][1]. + + + [1]: https://community.box.com/t5/Migrating-and-Previewing-Content/File-Types-and-Fonts-Supported-in-Box-Content-Preview/ta-p/327 + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extension: The file format for the thumbnail. + Example: "png" + :type extension: GetFileThumbnailByIdExtension + :param min_height: The minimum height of the thumbnail., defaults to None + :type min_height: Optional[int], optional + :param min_width: The minimum width of the thumbnail., defaults to None + :type min_width: Optional[int], optional + :param max_height: The maximum height of the thumbnail., defaults to None + :type max_height: Optional[int], optional + :param max_width: The maximum width of the thumbnail., defaults to None + :type max_width: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'min_height': to_string(min_height), + 'min_width': to_string(min_width), + 'max_height': to_string(max_height), + 'max_width': to_string(max_width), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/thumbnail.', + to_string(extension), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.BINARY, + auth=self.auth, + network_session=self.network_session, + ) + ) + if to_string(response.status) == '202': + return None + return response.content diff --git a/box_sdk_gen/managers/folder_classifications.py b/box_sdk_gen/managers/folder_classifications.py new file mode 100644 index 000000000..4192936a7 --- /dev/null +++ b/box_sdk_gen/managers/folder_classifications.py @@ -0,0 +1,331 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from typing import List + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.classification import Classification + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class UpdateClassificationOnFolderRequestBodyOpField(str, Enum): + REPLACE = 'replace' + + +class UpdateClassificationOnFolderRequestBodyPathField(str, Enum): + _BOX__SECURITY__CLASSIFICATION__KEY = '/Box__Security__Classification__Key' + + +class UpdateClassificationOnFolderRequestBody(BaseObject): + def __init__( + self, + value: str, + *, + op: UpdateClassificationOnFolderRequestBodyOpField = UpdateClassificationOnFolderRequestBodyOpField.REPLACE, + path: UpdateClassificationOnFolderRequestBodyPathField = UpdateClassificationOnFolderRequestBodyPathField._BOX__SECURITY__CLASSIFICATION__KEY, + **kwargs + ): + """ + :param value: The name of the classification to apply to this folder. + + To list the available classifications in an enterprise, + use the classification API to retrieve the + [classification template](e://get_metadata_templates_enterprise_securityClassification-6VMVochwUWo_schema) + which lists all available classification keys. + :type value: str + :param op: The value will always be `replace`., defaults to UpdateClassificationOnFolderRequestBodyOpField.REPLACE + :type op: UpdateClassificationOnFolderRequestBodyOpField, optional + :param path: Defines classifications + available in the enterprise., defaults to UpdateClassificationOnFolderRequestBodyPathField._BOX__SECURITY__CLASSIFICATION__KEY + :type path: UpdateClassificationOnFolderRequestBodyPathField, optional + """ + super().__init__(**kwargs) + self.value = value + self.op = op + self.path = path + + +class FolderClassificationsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_classification_on_folder( + self, + folder_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Classification: + """ + Retrieves the classification metadata instance that + + has been applied to a folder. + + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/folders/:id/enterprise_12345/securityClassification-6VMVochwUWo`. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Classification) + + def add_classification_to_folder( + self, + folder_id: str, + *, + box_security_classification_key: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Classification: + """ + Adds a classification to a folder by specifying the label of the + + classification to add. + + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/folders/:id/enterprise_12345/securityClassification-6VMVochwUWo`. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param box_security_classification_key: The name of the classification to apply to this folder. + + To list the available classifications in an enterprise, + use the classification API to retrieve the + [classification template](e://get_metadata_templates_enterprise_securityClassification-6VMVochwUWo_schema) + which lists all available classification keys., defaults to None + :type box_security_classification_key: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'Box__Security__Classification__Key': box_security_classification_key + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Classification) + + def update_classification_on_folder( + self, + folder_id: str, + request_body: List[UpdateClassificationOnFolderRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Classification: + """ + Updates a classification on a folder. + + The classification can only be updated if a classification has already been + + + applied to the folder before. When editing classifications, only values are + + + defined for the enterprise will be accepted. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param request_body: Request body of updateClassificationOnFolder method + :type request_body: List[UpdateClassificationOnFolderRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json-patch+json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Classification) + + def delete_classification_from_folder( + self, + folder_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes any classifications from a folder. + + This API can also be called by including the enterprise ID in the + + + URL explicitly, for example + + + `/folders/:id/enterprise_12345/securityClassification-6VMVochwUWo`. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/enterprise/securityClassification-6VMVochwUWo', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/folder_locks.py b/box_sdk_gen/managers/folder_locks.py new file mode 100644 index 000000000..c7c7e8e39 --- /dev/null +++ b/box_sdk_gen/managers/folder_locks.py @@ -0,0 +1,216 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.folder_locks import FolderLocks + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.folder_lock import FolderLock + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateFolderLockLockedOperations(BaseObject): + def __init__(self, move: bool, delete: bool, **kwargs): + """ + :param move: Whether moving the folder should be locked. + :type move: bool + :param delete: Whether deleting the folder should be locked. + :type delete: bool + """ + super().__init__(**kwargs) + self.move = move + self.delete = delete + + +class CreateFolderLockFolder(BaseObject): + def __init__(self, type: str, id: str, **kwargs): + """ + :param type: The content type the lock is being applied to. Only `folder` + is supported. + :type type: str + :param id: The ID of the folder. + :type id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class FolderLocksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_folder_locks( + self, + folder_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderLocks: + """ + Retrieves folder lock details for a given folder. + + You must be authenticated as the owner or co-owner of the folder to + + + use this endpoint. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + :type folder_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'folder_id': to_string(folder_id)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/folder_locks'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderLocks) + + def create_folder_lock( + self, + folder: CreateFolderLockFolder, + *, + locked_operations: Optional[CreateFolderLockLockedOperations] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderLock: + """ + Creates a folder lock on a folder, preventing it from being moved and/or + + deleted. + + + You must be authenticated as the owner or co-owner of the folder to + + + use this endpoint. + + :param folder: The folder to apply the lock to. + :type folder: CreateFolderLockFolder + :param locked_operations: The operations to lock for the folder. If `locked_operations` is + included in the request, both `move` and `delete` must also be + included and both set to `true`., defaults to None + :type locked_operations: Optional[CreateFolderLockLockedOperations], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'locked_operations': locked_operations, 'folder': folder} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/folder_locks'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderLock) + + def delete_folder_lock_by_id( + self, + folder_lock_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a folder lock on a given folder. + + You must be authenticated as the owner or co-owner of the folder to + + + use this endpoint. + + :param folder_lock_id: The ID of the folder lock. + Example: "12345" + :type folder_lock_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folder_locks/', + to_string(folder_lock_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/folder_metadata.py b/box_sdk_gen/managers/folder_metadata.py new file mode 100644 index 000000000..602effef4 --- /dev/null +++ b/box_sdk_gen/managers/folder_metadata.py @@ -0,0 +1,450 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from typing import List + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.metadatas import Metadatas + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.metadata_full import MetadataFull + +from box_sdk_gen.schemas.metadata_error import MetadataError + +from box_sdk_gen.schemas.metadata_instance_value import MetadataInstanceValue + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetFolderMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class CreateFolderMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class UpdateFolderMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class UpdateFolderMetadataByIdRequestBodyOpField(str, Enum): + ADD = 'add' + REPLACE = 'replace' + REMOVE = 'remove' + TEST = 'test' + MOVE = 'move' + COPY = 'copy' + + +class UpdateFolderMetadataByIdRequestBody(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'from_': 'from', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'from': 'from_', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + op: Optional[UpdateFolderMetadataByIdRequestBodyOpField] = None, + path: Optional[str] = None, + value: Optional[MetadataInstanceValue] = None, + from_: Optional[str] = None, + **kwargs + ): + """ + :param op: The type of change to perform on the template. Some + of these are hazardous as they will change existing templates., defaults to None + :type op: Optional[UpdateFolderMetadataByIdRequestBodyOpField], optional + :param path: The location in the metadata JSON object + to apply the changes to, in the format of a + [JSON-Pointer](https://tools.ietf.org/html/rfc6901). + + The path must always be prefixed with a `/` to represent the root + of the template. The characters `~` and `/` are reserved + characters and must be escaped in the key., defaults to None + :type path: Optional[str], optional + :param from_: The location in the metadata JSON object to move or copy a value + from. Required for `move` or `copy` operations and must be in the + format of a [JSON-Pointer](https://tools.ietf.org/html/rfc6901)., defaults to None + :type from_: Optional[str], optional + """ + super().__init__(**kwargs) + self.op = op + self.path = path + self.value = value + self.from_ = from_ + + +class DeleteFolderMetadataByIdScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class FolderMetadataManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_folder_metadata( + self, + folder_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Metadatas: + """ + Retrieves all metadata for a given folder. This can not be used on the root + + folder with ID `0`. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Metadatas) + + def get_folder_metadata_by_id( + self, + folder_id: str, + scope: GetFolderMetadataByIdScope, + template_key: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataFull: + """ + Retrieves the instance of a metadata template that has been applied to a + + folder. This can not be used on the root folder with ID `0`. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: GetFolderMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataFull) + + def create_folder_metadata_by_id( + self, + folder_id: str, + scope: CreateFolderMetadataByIdScope, + template_key: str, + request_body: Dict, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataFull: + """ + Applies an instance of a metadata template to a folder. + + In most cases only values that are present in the metadata template + + + will be accepted, except for the `global.properties` template which accepts + + + any key-value pair. + + + To display the metadata template in the Box web app the enterprise needs to be + + + configured to enable **Cascading Folder Level Metadata** for the user in the + + + admin console. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: CreateFolderMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param request_body: Request body of createFolderMetadataById method + :type request_body: Dict + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataFull) + + def update_folder_metadata_by_id( + self, + folder_id: str, + scope: UpdateFolderMetadataByIdScope, + template_key: str, + request_body: List[UpdateFolderMetadataByIdRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataFull: + """ + Updates a piece of metadata on a folder. + + The metadata instance can only be updated if the template has already been + + + applied to the folder before. When editing metadata, only values that match + + + the metadata template schema will be accepted. + + + The update is applied atomically. If any errors occur during the + + + application of the operations, the metadata instance will not be changed. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: UpdateFolderMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param request_body: Request body of updateFolderMetadataById method + :type request_body: List[UpdateFolderMetadataByIdRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json-patch+json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataFull) + + def delete_folder_metadata_by_id( + self, + folder_id: str, + scope: DeleteFolderMetadataByIdScope, + template_key: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a piece of folder metadata. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param scope: The scope of the metadata template. + Example: "global" + :type scope: DeleteFolderMetadataByIdScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/metadata/', + to_string(scope), + '/', + to_string(template_key), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/folder_watermarks.py b/box_sdk_gen/managers/folder_watermarks.py new file mode 100644 index 000000000..56ac61a99 --- /dev/null +++ b/box_sdk_gen/managers/folder_watermarks.py @@ -0,0 +1,214 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.watermark import Watermark + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class UpdateFolderWatermarkWatermarkImprintField(str, Enum): + DEFAULT = 'default' + + +class UpdateFolderWatermarkWatermark(BaseObject): + def __init__( + self, + *, + imprint: UpdateFolderWatermarkWatermarkImprintField = UpdateFolderWatermarkWatermarkImprintField.DEFAULT, + **kwargs + ): + """ + :param imprint: The type of watermark to apply. + + Currently only supports one option., defaults to UpdateFolderWatermarkWatermarkImprintField.DEFAULT + :type imprint: UpdateFolderWatermarkWatermarkImprintField, optional + """ + super().__init__(**kwargs) + self.imprint = imprint + + +class FolderWatermarksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_folder_watermark( + self, + folder_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Watermark: + """ + Retrieve the watermark for a folder. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/watermark', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Watermark) + + def update_folder_watermark( + self, + folder_id: str, + watermark: UpdateFolderWatermarkWatermark, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Watermark: + """ + Applies or update a watermark on a folder. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param watermark: The watermark to imprint on the folder. + :type watermark: UpdateFolderWatermarkWatermark + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'watermark': watermark} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/watermark', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Watermark) + + def delete_folder_watermark( + self, + folder_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes the watermark from a folder. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/watermark', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/folders.py b/box_sdk_gen/managers/folders.py new file mode 100644 index 000000000..50772507b --- /dev/null +++ b/box_sdk_gen/managers/folders.py @@ -0,0 +1,966 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.items import Items + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class GetFolderByIdSort(str, Enum): + ID = 'id' + NAME = 'name' + DATE = 'date' + SIZE = 'size' + + +class GetFolderByIdDirection(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class UpdateFolderByIdSyncState(str, Enum): + SYNCED = 'synced' + NOT_SYNCED = 'not_synced' + PARTIALLY_SYNCED = 'partially_synced' + + +class UpdateFolderByIdParent(BaseObject): + def __init__( + self, *, id: Optional[str] = None, user_id: Optional[str] = None, **kwargs + ): + """ + :param id: The ID of parent item., defaults to None + :type id: Optional[str], optional + :param user_id: The input for `user_id` is optional. Moving to non-root folder is not allowed when `user_id` is present. Parent folder id should be zero when `user_id` is provided., defaults to None + :type user_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.user_id = user_id + + +class UpdateFolderByIdSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class UpdateFolderByIdSharedLinkPermissionsField(BaseObject): + def __init__(self, *, can_download: Optional[bool] = None, **kwargs): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + + +class UpdateFolderByIdSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateFolderByIdSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[UpdateFolderByIdSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the folder (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[UpdateFolderByIdSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class UpdateFolderByIdFolderUploadEmailAccessField(str, Enum): + OPEN = 'open' + COLLABORATORS = 'collaborators' + + +class UpdateFolderByIdFolderUploadEmail(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateFolderByIdFolderUploadEmailAccessField] = None, + **kwargs + ): + """ + :param access: When this parameter has been set, users can email files + to the email address that has been automatically + created for this folder. + + To create an email address, set this property either when + creating or updating the folder. + + When set to `collaborators`, only emails from registered email + addresses for collaborators will be accepted. This includes + any email aliases a user might have registered. + + When set to `open` it will accept emails from any email + address., defaults to None + :type access: Optional[UpdateFolderByIdFolderUploadEmailAccessField], optional + """ + super().__init__(**kwargs) + self.access = access + + +class UpdateFolderByIdCollections(BaseObject): + def __init__( + self, *, id: Optional[str] = None, type: Optional[str] = None, **kwargs + ): + """ + :param id: The unique identifier for this object., defaults to None + :type id: Optional[str], optional + :param type: The type for this object., defaults to None + :type type: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class GetFolderItemsSort(str, Enum): + ID = 'id' + NAME = 'name' + DATE = 'date' + SIZE = 'size' + + +class GetFolderItemsDirection(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class CreateFolderParent(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of parent folder. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class CreateFolderFolderUploadEmailAccessField(str, Enum): + OPEN = 'open' + COLLABORATORS = 'collaborators' + + +class CreateFolderFolderUploadEmail(BaseObject): + def __init__( + self, + *, + access: Optional[CreateFolderFolderUploadEmailAccessField] = None, + **kwargs + ): + """ + :param access: When this parameter has been set, users can email files + to the email address that has been automatically + created for this folder. + + To create an email address, set this property either when + creating or updating the folder. + + When set to `collaborators`, only emails from registered email + addresses for collaborators will be accepted. This includes + any email aliases a user might have registered. + + When set to `open` it will accept emails from any email + address., defaults to None + :type access: Optional[CreateFolderFolderUploadEmailAccessField], optional + """ + super().__init__(**kwargs) + self.access = access + + +class CreateFolderSyncState(str, Enum): + SYNCED = 'synced' + NOT_SYNCED = 'not_synced' + PARTIALLY_SYNCED = 'partially_synced' + + +class CopyFolderParent(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of parent folder. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class FoldersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_folder_by_id( + self, + folder_id: str, + *, + fields: Optional[List[str]] = None, + sort: Optional[GetFolderByIdSort] = None, + direction: Optional[GetFolderByIdDirection] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + if_none_match: Optional[str] = None, + boxapi: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + """ + Retrieves details for a folder, including the first 100 entries + + in the folder. + + + Passing `sort`, `direction`, `offset`, and `limit` + + + parameters in query allows you to manage the + + + list of returned + + + [folder items](r://folder--full#param-item-collection). + + + To fetch more items within the folder, use the + + + [Get items in a folder](e://get-folders-id-items) endpoint. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested. + + Additionally this field can be used to query any metadata + applied to the file by specifying the `metadata` field as well + as the scope and key of the template to retrieve, for example + `?fields=metadata.enterprise_12345.contractTemplate`., defaults to None + :type fields: Optional[List[str]], optional + :param sort: Defines the **second** attribute by which items + are sorted. + + The folder type affects the way the items + are sorted: + + * **Standard folder**: + Items are always sorted by + their `type` first, with + folders listed before files, + and files listed + before web links. + + * **Root folder**: + This parameter is not supported + for marker-based pagination + on the root folder + + (the folder with an `id` of `0`). + + * **Shared folder with parent path + to the associated folder visible to + the collaborator**: + Items are always sorted by + their `type` first, with + folders listed before files, + and files listed + before web links., defaults to None + :type sort: Optional[GetFolderByIdSort], optional + :param direction: The direction to sort results in. This can be either in alphabetical ascending + (`ASC`) or descending (`DESC`) order., defaults to None + :type direction: Optional[GetFolderByIdDirection], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param if_none_match: Ensures an item is only returned if it has changed. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `304 Not Modified` if the item has not + changed since., defaults to None + :type if_none_match: Optional[str], optional + :param boxapi: The URL, and optional password, for the shared link of this item. + + This header can be used to access items that have not been + explicitly shared with a user. + + Use the format `shared_link=[link]` or if a password is required then + use `shared_link=[link]&shared_link_password=[password]`. + + This header can be used on the file or folder shared, as well as on any files + or folders nested within the item., defaults to None + :type boxapi: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'sort': to_string(sort), + 'direction': to_string(direction), + 'offset': to_string(offset), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params( + { + 'if-none-match': to_string(if_none_match), + 'boxapi': to_string(boxapi), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) + + def update_folder_by_id( + self, + folder_id: str, + *, + name: Optional[str] = None, + description: Optional[str] = None, + sync_state: Optional[UpdateFolderByIdSyncState] = None, + can_non_owners_invite: Optional[bool] = None, + parent: Optional[UpdateFolderByIdParent] = None, + shared_link: Optional[UpdateFolderByIdSharedLink] = None, + folder_upload_email: Union[ + Optional[UpdateFolderByIdFolderUploadEmail], NullValue + ] = None, + tags: Optional[List[str]] = None, + is_collaboration_restricted_to_enterprise: Optional[bool] = None, + collections: Union[ + Optional[List[UpdateFolderByIdCollections]], NullValue + ] = None, + can_non_owners_view_collaborators: Optional[bool] = None, + fields: Optional[List[str]] = None, + if_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + r""" + Updates a folder. This can be also be used to move the folder, + + create shared links, update collaborations, and more. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param name: The optional new name for this folder. + + The following restrictions to folder names apply: names containing + non-printable ASCII characters, forward and backward slashes + (`/`, `\`), names with trailing spaces, and names `.` and `..` are + not allowed. + + Folder names must be unique within their parent folder. The name check is case-insensitive, + so a folder named `New Folder` cannot be created in a parent folder that already contains + a folder named `new folder`., defaults to None + :type name: Optional[str], optional + :param description: The optional description of this folder., defaults to None + :type description: Optional[str], optional + :param sync_state: Specifies whether a folder should be synced to a + user's device or not. This is used by Box Sync + (discontinued) and is not used by Box Drive., defaults to None + :type sync_state: Optional[UpdateFolderByIdSyncState], optional + :param can_non_owners_invite: Specifies if users who are not the owner + of the folder can invite new collaborators to the folder., defaults to None + :type can_non_owners_invite: Optional[bool], optional + :param tags: The tags for this item. These tags are shown in + the Box web app and mobile apps next to an item. + + To add or remove a tag, retrieve the item's current tags, + modify them, and then update this field. + + There is a limit of 100 tags per item, and 10,000 + unique tags per enterprise., defaults to None + :type tags: Optional[List[str]], optional + :param is_collaboration_restricted_to_enterprise: Specifies if new invites to this folder are restricted to users + within the enterprise. This does not affect existing + collaborations., defaults to None + :type is_collaboration_restricted_to_enterprise: Optional[bool], optional + :param collections: An array of collections to make this folder + a member of. Currently + we only support the `favorites` collection. + + To get the ID for a collection, use the + [List all collections][1] endpoint. + + Passing an empty array `[]` or `null` will remove + the folder from all collections. + + [1]: e://get-collections, defaults to None + :type collections: Union[Optional[List[UpdateFolderByIdCollections]], NullValue], optional + :param can_non_owners_view_collaborators: Restricts collaborators who are not the owner of + this folder from viewing other collaborations on + this folder. + + It also restricts non-owners from inviting new + collaborators. + + When setting this field to `false`, it is required + to also set `can_non_owners_invite_collaborators` to + `false` if it has not already been set., defaults to None + :type can_non_owners_view_collaborators: Optional[bool], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'description': description, + 'sync_state': sync_state, + 'can_non_owners_invite': can_non_owners_invite, + 'parent': parent, + 'shared_link': shared_link, + 'folder_upload_email': folder_upload_email, + 'tags': tags, + 'is_collaboration_restricted_to_enterprise': ( + is_collaboration_restricted_to_enterprise + ), + 'collections': collections, + 'can_non_owners_view_collaborators': can_non_owners_view_collaborators, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + {'if-match': to_string(if_match), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) + + def delete_folder_by_id( + self, + folder_id: str, + *, + recursive: Optional[bool] = None, + if_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a folder, either permanently or by moving it to + + the trash. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param recursive: Delete a folder that is not empty by recursively deleting the + folder and all of its content., defaults to None + :type recursive: Optional[bool], optional + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'recursive': to_string(recursive)} + ) + headers_map: Dict[str, str] = prepare_params( + {'if-match': to_string(if_match), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + ] + ), + method='DELETE', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_folder_items( + self, + folder_id: str, + *, + fields: Optional[List[str]] = None, + usemarker: Optional[bool] = None, + marker: Optional[str] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + sort: Optional[GetFolderItemsSort] = None, + direction: Optional[GetFolderItemsDirection] = None, + boxapi: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Items: + """ + Retrieves a page of items in a folder. These items can be files, + + folders, and web links. + + + To request more information about the folder itself, like its size, + + + use the [Get a folder](#get-folders-id) endpoint instead. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested. + + Additionally this field can be used to query any metadata + applied to the file by specifying the `metadata` field as well + as the scope and key of the template to retrieve, for example + `?fields=metadata.enterprise_12345.contractTemplate`., defaults to None + :type fields: Optional[List[str]], optional + :param usemarker: Specifies whether to use marker-based pagination instead of + offset-based pagination. Only one pagination method can + be used at a time. + + By setting this value to true, the API will return a `marker` field + that can be passed as a parameter to this endpoint to get the next + page of the response., defaults to None + :type usemarker: Optional[bool], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param sort: Defines the **second** attribute by which items + are sorted. + + The folder type affects the way the items + are sorted: + + * **Standard folder**: + Items are always sorted by + their `type` first, with + folders listed before files, + and files listed + before web links. + + * **Root folder**: + This parameter is not supported + for marker-based pagination + on the root folder + + (the folder with an `id` of `0`). + + * **Shared folder with parent path + to the associated folder visible to + the collaborator**: + Items are always sorted by + their `type` first, with + folders listed before files, + and files listed + before web links., defaults to None + :type sort: Optional[GetFolderItemsSort], optional + :param direction: The direction to sort results in. This can be either in alphabetical ascending + (`ASC`) or descending (`DESC`) order., defaults to None + :type direction: Optional[GetFolderItemsDirection], optional + :param boxapi: The URL, and optional password, for the shared link of this item. + + This header can be used to access items that have not been + explicitly shared with a user. + + Use the format `shared_link=[link]` or if a password is required then + use `shared_link=[link]&shared_link_password=[password]`. + + This header can be used on the file or folder shared, as well as on any files + or folders nested within the item., defaults to None + :type boxapi: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'usemarker': to_string(usemarker), + 'marker': to_string(marker), + 'offset': to_string(offset), + 'limit': to_string(limit), + 'sort': to_string(sort), + 'direction': to_string(direction), + } + ) + headers_map: Dict[str, str] = prepare_params( + {'boxapi': to_string(boxapi), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/items', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Items) + + def create_folder( + self, + name: str, + parent: CreateFolderParent, + *, + folder_upload_email: Optional[CreateFolderFolderUploadEmail] = None, + sync_state: Optional[CreateFolderSyncState] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + r""" + Creates a new empty folder within the specified parent folder. + :param name: The name for the new folder. + + The following restrictions to folder names apply: names containing + non-printable ASCII characters, forward and backward slashes + (`/`, `\`), names with trailing spaces, and names `.` and `..` are + not allowed. + + Folder names must be unique within their parent folder. The name check is case-insensitive, + so a folder named `New Folder` cannot be created in a parent folder that already contains + a folder named `new folder`. + :type name: str + :param parent: The parent folder to create the new folder within. + :type parent: CreateFolderParent + :param sync_state: Specifies whether a folder should be synced to a + user's device or not. This is used by Box Sync + (discontinued) and is not used by Box Drive., defaults to None + :type sync_state: Optional[CreateFolderSyncState], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'parent': parent, + 'folder_upload_email': folder_upload_email, + 'sync_state': sync_state, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/folders']), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) + + def copy_folder( + self, + folder_id: str, + parent: CopyFolderParent, + *, + name: Optional[str] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + r""" + Creates a copy of a folder within a destination folder. + + The original folder will not be changed. + + :param folder_id: The unique identifier of the folder to copy. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder with the ID `0` can not be copied. + Example: "0" + :type folder_id: str + :param parent: The destination folder to copy the folder to. + :type parent: CopyFolderParent + :param name: An optional new name for the copied folder. + + There are some restrictions to the file name. Names containing + non-printable ASCII characters, forward and backward slashes + (`/`, `\`), as well as names with trailing spaces are + prohibited. + + Additionally, the names `.` and `..` are + not allowed either., defaults to None + :type name: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name, 'parent': parent} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/copy', + ] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) diff --git a/box_sdk_gen/managers/groups.py b/box_sdk_gen/managers/groups.py new file mode 100644 index 000000000..200116ba6 --- /dev/null +++ b/box_sdk_gen/managers/groups.py @@ -0,0 +1,450 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.groups import Groups + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.group_full import GroupFull + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateGroupInvitabilityLevel(str, Enum): + ADMINS_ONLY = 'admins_only' + ADMINS_AND_MEMBERS = 'admins_and_members' + ALL_MANAGED_USERS = 'all_managed_users' + + +class CreateGroupMemberViewabilityLevel(str, Enum): + ADMINS_ONLY = 'admins_only' + ADMINS_AND_MEMBERS = 'admins_and_members' + ALL_MANAGED_USERS = 'all_managed_users' + + +class UpdateGroupByIdInvitabilityLevel(str, Enum): + ADMINS_ONLY = 'admins_only' + ADMINS_AND_MEMBERS = 'admins_and_members' + ALL_MANAGED_USERS = 'all_managed_users' + + +class UpdateGroupByIdMemberViewabilityLevel(str, Enum): + ADMINS_ONLY = 'admins_only' + ADMINS_AND_MEMBERS = 'admins_and_members' + ALL_MANAGED_USERS = 'all_managed_users' + + +class GroupsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_groups( + self, + *, + filter_term: Optional[str] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Groups: + """ + Retrieves all of the groups for a given enterprise. The user + + must have admin permissions to inspect enterprise's groups. + + :param filter_term: Limits the results to only groups whose `name` starts + with the search term., defaults to None + :type filter_term: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'filter_term': to_string(filter_term), + 'fields': to_string(fields), + 'limit': to_string(limit), + 'offset': to_string(offset), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/groups']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Groups) + + def create_group( + self, + name: str, + *, + provenance: Optional[str] = None, + external_sync_identifier: Optional[str] = None, + description: Optional[str] = None, + invitability_level: Optional[CreateGroupInvitabilityLevel] = None, + member_viewability_level: Optional[CreateGroupMemberViewabilityLevel] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupFull: + """ + Creates a new group of users in an enterprise. Only users with admin + + permissions can create new groups. + + :param name: The name of the new group to be created. This name must be unique + within the enterprise. + :type name: str + :param provenance: Keeps track of which external source this group is + coming, for example `Active Directory`, or `Okta`. + + Setting this will also prevent Box admins from editing + the group name and its members directly via the Box + web application. + + This is desirable for one-way syncing of groups., defaults to None + :type provenance: Optional[str], optional + :param external_sync_identifier: An arbitrary identifier that can be used by + external group sync tools to link this Box Group to + an external group. + + Example values of this field + could be an **Active Directory Object ID** or a **Google + Group ID**. + + We recommend you use of this field in + order to avoid issues when group names are updated in + either Box or external systems., defaults to None + :type external_sync_identifier: Optional[str], optional + :param description: A human readable description of the group., defaults to None + :type description: Optional[str], optional + :param invitability_level: Specifies who can invite the group to collaborate + on folders. + + When set to `admins_only` the enterprise admin, co-admins, + and the group's admin can invite the group. + + When set to `admins_and_members` all the admins listed + above and group members can invite the group. + + When set to `all_managed_users` all managed users in the + enterprise can invite the group., defaults to None + :type invitability_level: Optional[CreateGroupInvitabilityLevel], optional + :param member_viewability_level: Specifies who can see the members of the group. + + * `admins_only` - the enterprise admin, co-admins, group's + group admin. + * `admins_and_members` - all admins and group members. + * `all_managed_users` - all managed users in the + enterprise., defaults to None + :type member_viewability_level: Optional[CreateGroupMemberViewabilityLevel], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'provenance': provenance, + 'external_sync_identifier': external_sync_identifier, + 'description': description, + 'invitability_level': invitability_level, + 'member_viewability_level': member_viewability_level, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/groups']), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupFull) + + def get_group_by_id( + self, + group_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupFull: + """ + Retrieves information about a group. Only members of this + + group or users with admin-level permissions will be able to + + + use this API. + + :param group_id: The ID of the group. + Example: "57645" + :type group_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/groups/', + to_string(group_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupFull) + + def update_group_by_id( + self, + group_id: str, + *, + name: Optional[str] = None, + provenance: Optional[str] = None, + external_sync_identifier: Optional[str] = None, + description: Optional[str] = None, + invitability_level: Optional[UpdateGroupByIdInvitabilityLevel] = None, + member_viewability_level: Optional[ + UpdateGroupByIdMemberViewabilityLevel + ] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupFull: + """ + Updates a specific group. Only admins of this + + group or users with admin-level permissions will be able to + + + use this API. + + :param group_id: The ID of the group. + Example: "57645" + :type group_id: str + :param name: The name of the new group to be created. Must be unique within the + enterprise., defaults to None + :type name: Optional[str], optional + :param provenance: Keeps track of which external source this group is + coming, for example `Active Directory`, or `Okta`. + + Setting this will also prevent Box admins from editing + the group name and its members directly via the Box + web application. + + This is desirable for one-way syncing of groups., defaults to None + :type provenance: Optional[str], optional + :param external_sync_identifier: An arbitrary identifier that can be used by + external group sync tools to link this Box Group to + an external group. + + Example values of this field + could be an **Active Directory Object ID** or a **Google + Group ID**. + + We recommend you use of this field in + order to avoid issues when group names are updated in + either Box or external systems., defaults to None + :type external_sync_identifier: Optional[str], optional + :param description: A human readable description of the group., defaults to None + :type description: Optional[str], optional + :param invitability_level: Specifies who can invite the group to collaborate + on folders. + + When set to `admins_only` the enterprise admin, co-admins, + and the group's admin can invite the group. + + When set to `admins_and_members` all the admins listed + above and group members can invite the group. + + When set to `all_managed_users` all managed users in the + enterprise can invite the group., defaults to None + :type invitability_level: Optional[UpdateGroupByIdInvitabilityLevel], optional + :param member_viewability_level: Specifies who can see the members of the group. + + * `admins_only` - the enterprise admin, co-admins, group's + group admin. + * `admins_and_members` - all admins and group members. + * `all_managed_users` - all managed users in the + enterprise., defaults to None + :type member_viewability_level: Optional[UpdateGroupByIdMemberViewabilityLevel], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'provenance': provenance, + 'external_sync_identifier': external_sync_identifier, + 'description': description, + 'invitability_level': invitability_level, + 'member_viewability_level': member_viewability_level, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/groups/', + to_string(group_id), + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupFull) + + def delete_group_by_id( + self, group_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Permanently deletes a group. Only users with + + admin-level permissions will be able to use this API. + + :param group_id: The ID of the group. + Example: "57645" + :type group_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/groups/', + to_string(group_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/hub_collaborations.py b/box_sdk_gen/managers/hub_collaborations.py new file mode 100644 index 000000000..158617823 --- /dev/null +++ b/box_sdk_gen/managers/hub_collaborations.py @@ -0,0 +1,360 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.hub_collaborations_v2025_r0 import ( + HubCollaborationsV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_v2025_r0 import ( + HubCollaborationV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_create_request_v2025_r0 import ( + HubCollaborationCreateRequestV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_update_request_v2025_r0 import ( + HubCollaborationUpdateRequestV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateHubCollaborationV2025R0HubTypeField(str, Enum): + HUBS = 'hubs' + + +class CreateHubCollaborationV2025R0Hub(BaseObject): + _discriminator = 'type', {'hubs'} + + def __init__( + self, + id: str, + *, + type: CreateHubCollaborationV2025R0HubTypeField = CreateHubCollaborationV2025R0HubTypeField.HUBS, + **kwargs + ): + """ + :param id: ID of the object. + :type id: str + :param type: The value will always be `hubs`., defaults to CreateHubCollaborationV2025R0HubTypeField.HUBS + :type type: CreateHubCollaborationV2025R0HubTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateHubCollaborationV2025R0AccessibleBy(BaseObject): + def __init__( + self, + type: str, + *, + id: Optional[str] = None, + login: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of collaborator to invite. + Possible values are `user` or `group`. + :type type: str + :param id: The ID of the user or group. + + Alternatively, use `login` to specify a user by email + address., defaults to None + :type id: Optional[str], optional + :param login: The email address of the user who gets access to the item. + + Alternatively, use `id` to specify a user by user ID., defaults to None + :type login: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.login = login + + +class HubCollaborationsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_hub_collaborations_v2025_r0( + self, + hub_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubCollaborationsV2025R0: + """ + Retrieves all collaborations for a Box Hub. + :param hub_id: The unique identifier that represent a hub. + + The ID for any hub can be determined + by visiting this hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + :type hub_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'hub_id': to_string(hub_id), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/hub_collaborations'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubCollaborationsV2025R0) + + def create_hub_collaboration_v2025_r0( + self, + hub: CreateHubCollaborationV2025R0Hub, + accessible_by: CreateHubCollaborationV2025R0AccessibleBy, + role: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubCollaborationV2025R0: + """ + Adds a collaboration for a single user or a single group to a Box Hub. + + Collaborations can be created using email address, user IDs, or group IDs. + + :param hub: Box Hubs reference. + :type hub: CreateHubCollaborationV2025R0Hub + :param accessible_by: The user or group who gets access to the item. + :type accessible_by: CreateHubCollaborationV2025R0AccessibleBy + :param role: The level of access granted to a Box Hub. + Possible values are `editor`, `viewer`, and `co-owner`. + :type role: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'hub': hub, 'accessible_by': accessible_by, 'role': role} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/hub_collaborations'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubCollaborationV2025R0) + + def get_hub_collaboration_by_id_v2025_r0( + self, + hub_collaboration_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubCollaborationV2025R0: + """ + Retrieves details for a Box Hub collaboration by collaboration ID. + :param hub_collaboration_id: The ID of the hub collaboration. + Example: "1234" + :type hub_collaboration_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hub_collaborations/', + to_string(hub_collaboration_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubCollaborationV2025R0) + + def update_hub_collaboration_by_id_v2025_r0( + self, + hub_collaboration_id: str, + *, + role: Optional[str] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubCollaborationV2025R0: + """ + Updates a Box Hub collaboration. + + Can be used to change the Box Hub role. + + :param hub_collaboration_id: The ID of the hub collaboration. + Example: "1234" + :type hub_collaboration_id: str + :param role: The level of access granted to a Box Hub. + Possible values are `editor`, `viewer`, and `co-owner`., defaults to None + :type role: Optional[str], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'role': role} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hub_collaborations/', + to_string(hub_collaboration_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubCollaborationV2025R0) + + def delete_hub_collaboration_by_id_v2025_r0( + self, + hub_collaboration_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a single Box Hub collaboration. + :param hub_collaboration_id: The ID of the hub collaboration. + Example: "1234" + :type hub_collaboration_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hub_collaborations/', + to_string(hub_collaboration_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/hub_items.py b/box_sdk_gen/managers/hub_items.py new file mode 100644 index 000000000..a05cc2ef5 --- /dev/null +++ b/box_sdk_gen/managers/hub_items.py @@ -0,0 +1,177 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from typing import List + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.hub_item_operation_v2025_r0 import ( + HubItemOperationV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_items_v2025_r0 import HubItemsV2025R0 + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_items_manage_response_v2025_r0 import ( + HubItemsManageResponseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_items_manage_request_v2025_r0 import ( + HubItemsManageRequestV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class HubItemsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_hub_items_v2025_r0( + self, + hub_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubItemsV2025R0: + """ + Retrieves all items associated with a Box Hub. + :param hub_id: The unique identifier that represent a hub. + + The ID for any hub can be determined + by visiting this hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + :type hub_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'hub_id': to_string(hub_id), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/hub_items'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubItemsV2025R0) + + def manage_hub_items_v2025_r0( + self, + hub_id: str, + *, + operations: Optional[List[HubItemOperationV2025R0]] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubItemsManageResponseV2025R0: + """ + Adds and/or removes Box Hub items from a Box Hub. + :param hub_id: The unique identifier that represent a hub. + + The ID for any hub can be determined + by visiting this hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + Example: "12345" + :type hub_id: str + :param operations: List of operations to perform on Box Hub items., defaults to None + :type operations: Optional[List[HubItemOperationV2025R0]], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'operations': operations} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hubs/', + to_string(hub_id), + '/manage_items', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubItemsManageResponseV2025R0) diff --git a/box_sdk_gen/managers/hubs.py b/box_sdk_gen/managers/hubs.py new file mode 100644 index 000000000..47669e810 --- /dev/null +++ b/box_sdk_gen/managers/hubs.py @@ -0,0 +1,475 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.hubs_v2025_r0 import HubsV2025R0 + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_v2025_r0 import HubV2025R0 + +from box_sdk_gen.schemas.v2025_r0.hub_create_request_v2025_r0 import ( + HubCreateRequestV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_update_request_v2025_r0 import ( + HubUpdateRequestV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_copy_request_v2025_r0 import HubCopyRequestV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetHubsV2025R0Direction(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class GetEnterpriseHubsV2025R0Direction(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class HubsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_hubs_v2025_r0( + self, + *, + query: Optional[str] = None, + scope: Optional[str] = None, + sort: Optional[str] = None, + direction: Optional[GetHubsV2025R0Direction] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubsV2025R0: + """ + Retrieves all Box Hubs for requesting user. + :param query: The query string to search for Box Hubs., defaults to None + :type query: Optional[str], optional + :param scope: The scope of the Box Hubs to retrieve. Possible values include `editable`, + `view_only`, and `all`. Default is `all`., defaults to None + :type scope: Optional[str], optional + :param sort: The field to sort results by. + Possible values include `name`, `updated_at`, + `last_accessed_at`, `view_count`, and `relevance`. + Default is `relevance`., defaults to None + :type sort: Optional[str], optional + :param direction: The direction to sort results in. This can be either in alphabetical ascending + (`ASC`) or descending (`DESC`) order., defaults to None + :type direction: Optional[GetHubsV2025R0Direction], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'query': to_string(query), + 'scope': to_string(scope), + 'sort': to_string(sort), + 'direction': to_string(direction), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/hubs']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubsV2025R0) + + def create_hub_v2025_r0( + self, + title: str, + *, + description: Optional[str] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubV2025R0: + """ + Creates a new Box Hub. + :param title: Title of the Box Hub. It cannot be empty and should be less than 50 characters. + :type title: str + :param description: Description of the Box Hub., defaults to None + :type description: Optional[str], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'title': title, 'description': description} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/hubs']), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubV2025R0) + + def get_enterprise_hubs_v2025_r0( + self, + *, + query: Optional[str] = None, + sort: Optional[str] = None, + direction: Optional[GetEnterpriseHubsV2025R0Direction] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubsV2025R0: + """ + Retrieves all Box Hubs for a given enterprise. + + Admins or Hub Co-admins of an enterprise + + + with GCM scope can make this call. + + :param query: The query string to search for Box Hubs., defaults to None + :type query: Optional[str], optional + :param sort: The field to sort results by. + Possible values include `name`, `updated_at`, + `last_accessed_at`, `view_count`, and `relevance`. + Default is `relevance`., defaults to None + :type sort: Optional[str], optional + :param direction: The direction to sort results in. This can be either in alphabetical ascending + (`ASC`) or descending (`DESC`) order., defaults to None + :type direction: Optional[GetEnterpriseHubsV2025R0Direction], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'query': to_string(query), + 'sort': to_string(sort), + 'direction': to_string(direction), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/enterprise_hubs'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubsV2025R0) + + def get_hub_by_id_v2025_r0( + self, + hub_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubV2025R0: + """ + Retrieves details for a Box Hub by its ID. + :param hub_id: The unique identifier that represent a hub. + + The ID for any hub can be determined + by visiting this hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + Example: "12345" + :type hub_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hubs/', + to_string(hub_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubV2025R0) + + def update_hub_by_id_v2025_r0( + self, + hub_id: str, + *, + title: Optional[str] = None, + description: Optional[str] = None, + is_ai_enabled: Optional[bool] = None, + is_collaboration_restricted_to_enterprise: Optional[bool] = None, + can_non_owners_invite: Optional[bool] = None, + can_shared_link_be_created: Optional[bool] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubV2025R0: + """ + Updates a Box Hub. Can be used to change title, description, or Box Hub settings. + :param hub_id: The unique identifier that represent a hub. + + The ID for any hub can be determined + by visiting this hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + Example: "12345" + :type hub_id: str + :param title: Title of the Box Hub. It cannot be empty and should be less than 50 characters., defaults to None + :type title: Optional[str], optional + :param description: Description of the Box Hub., defaults to None + :type description: Optional[str], optional + :param is_ai_enabled: Indicates if AI features are enabled for the Box Hub., defaults to None + :type is_ai_enabled: Optional[bool], optional + :param is_collaboration_restricted_to_enterprise: Indicates if collaboration is restricted to the enterprise., defaults to None + :type is_collaboration_restricted_to_enterprise: Optional[bool], optional + :param can_non_owners_invite: Indicates if non-owners can invite others to the Box Hub., defaults to None + :type can_non_owners_invite: Optional[bool], optional + :param can_shared_link_be_created: Indicates if a shared link can be created for the Box Hub., defaults to None + :type can_shared_link_be_created: Optional[bool], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'title': title, + 'description': description, + 'is_ai_enabled': is_ai_enabled, + 'is_collaboration_restricted_to_enterprise': ( + is_collaboration_restricted_to_enterprise + ), + 'can_non_owners_invite': can_non_owners_invite, + 'can_shared_link_be_created': can_shared_link_be_created, + } + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hubs/', + to_string(hub_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubV2025R0) + + def delete_hub_by_id_v2025_r0( + self, + hub_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a single Box Hub. + :param hub_id: The unique identifier that represent a hub. + + The ID for any hub can be determined + by visiting this hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + Example: "12345" + :type hub_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hubs/', + to_string(hub_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def copy_hub_v2025_r0( + self, + hub_id: str, + *, + title: Optional[str] = None, + description: Optional[str] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> HubV2025R0: + """ + Creates a copy of a Box Hub. + + The original Box Hub will not be modified. + + :param hub_id: The unique identifier that represent a hub. + + The ID for any hub can be determined + by visiting this hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + Example: "12345" + :type hub_id: str + :param title: Title of the Box Hub. It cannot be empty and should be less than 50 characters., defaults to None + :type title: Optional[str], optional + :param description: Description of the Box Hub., defaults to None + :type description: Optional[str], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'title': title, 'description': description} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/hubs/', + to_string(hub_id), + '/copy', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, HubV2025R0) diff --git a/box_sdk_gen/managers/integration_mappings.py b/box_sdk_gen/managers/integration_mappings.py new file mode 100644 index 000000000..49245b679 --- /dev/null +++ b/box_sdk_gen/managers/integration_mappings.py @@ -0,0 +1,504 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.integration_mapping_partner_item_slack import ( + IntegrationMappingPartnerItemSlack, +) + +from box_sdk_gen.schemas.integration_mapping_partner_item_teams_create_request import ( + IntegrationMappingPartnerItemTeamsCreateRequest, +) + +from box_sdk_gen.schemas.integration_mappings import IntegrationMappings + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.integration_mapping import IntegrationMapping + +from box_sdk_gen.schemas.integration_mapping_slack_create_request import ( + IntegrationMappingSlackCreateRequest, +) + +from box_sdk_gen.schemas.integration_mapping_box_item_slack import ( + IntegrationMappingBoxItemSlack, +) + +from box_sdk_gen.schemas.integration_mapping_slack_options import ( + IntegrationMappingSlackOptions, +) + +from box_sdk_gen.schemas.integration_mappings_teams import IntegrationMappingsTeams + +from box_sdk_gen.schemas.integration_mapping_teams import IntegrationMappingTeams + +from box_sdk_gen.schemas.integration_mapping_teams_create_request import ( + IntegrationMappingTeamsCreateRequest, +) + +from box_sdk_gen.schemas.folder_reference import FolderReference + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetSlackIntegrationMappingPartnerItemType(str, Enum): + CHANNEL = 'channel' + + +class GetSlackIntegrationMappingBoxItemType(str, Enum): + FOLDER = 'folder' + + +class GetTeamsIntegrationMappingPartnerItemType(str, Enum): + CHANNEL = 'channel' + TEAM = 'team' + + +class GetTeamsIntegrationMappingBoxItemType(str, Enum): + FOLDER = 'folder' + + +class IntegrationMappingsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_slack_integration_mapping( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + partner_item_type: Optional[GetSlackIntegrationMappingPartnerItemType] = None, + partner_item_id: Optional[str] = None, + box_item_id: Optional[str] = None, + box_item_type: Optional[GetSlackIntegrationMappingBoxItemType] = None, + is_manually_created: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> IntegrationMappings: + """ + Lists [Slack integration mappings](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack) in a users' enterprise. + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param partner_item_type: Mapped item type, for which the mapping should be returned., defaults to None + :type partner_item_type: Optional[GetSlackIntegrationMappingPartnerItemType], optional + :param partner_item_id: ID of the mapped item, + for which the mapping should be returned., defaults to None + :type partner_item_id: Optional[str], optional + :param box_item_id: Box item ID, for which the mappings should be returned., defaults to None + :type box_item_id: Optional[str], optional + :param box_item_type: Box item type, for + which the mappings should be returned., defaults to None + :type box_item_type: Optional[GetSlackIntegrationMappingBoxItemType], optional + :param is_manually_created: Whether the mapping has been manually created., defaults to None + :type is_manually_created: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'marker': to_string(marker), + 'limit': to_string(limit), + 'partner_item_type': to_string(partner_item_type), + 'partner_item_id': to_string(partner_item_id), + 'box_item_id': to_string(box_item_id), + 'box_item_type': to_string(box_item_type), + 'is_manually_created': to_string(is_manually_created), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/slack', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, IntegrationMappings) + + def create_slack_integration_mapping( + self, + partner_item: IntegrationMappingPartnerItemSlack, + box_item: IntegrationMappingBoxItemSlack, + *, + options: Optional[IntegrationMappingSlackOptions] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> IntegrationMapping: + """ + Creates a [Slack integration mapping](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack) + + by mapping a Slack channel to a Box item. + + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'partner_item': partner_item, + 'box_item': box_item, + 'options': options, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/slack', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, IntegrationMapping) + + def update_slack_integration_mapping_by_id( + self, + integration_mapping_id: str, + *, + box_item: Optional[IntegrationMappingBoxItemSlack] = None, + options: Optional[IntegrationMappingSlackOptions] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> IntegrationMapping: + """ + Updates a [Slack integration mapping](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack). + + Supports updating the Box folder ID and options. + + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param integration_mapping_id: An ID of an integration mapping. + Example: "11235432" + :type integration_mapping_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'box_item': box_item, 'options': options} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/slack/', + to_string(integration_mapping_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, IntegrationMapping) + + def delete_slack_integration_mapping_by_id( + self, + integration_mapping_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a [Slack integration mapping](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack). + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param integration_mapping_id: An ID of an integration mapping. + Example: "11235432" + :type integration_mapping_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/slack/', + to_string(integration_mapping_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_teams_integration_mapping( + self, + *, + partner_item_type: Optional[GetTeamsIntegrationMappingPartnerItemType] = None, + partner_item_id: Optional[str] = None, + box_item_id: Optional[str] = None, + box_item_type: Optional[GetTeamsIntegrationMappingBoxItemType] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> IntegrationMappingsTeams: + """ + Lists [Teams integration mappings](https://support.box.com/hc/en-us/articles/360044681474-Using-Box-for-Teams) in a users' enterprise. + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param partner_item_type: Mapped item type, for which the mapping should be returned., defaults to None + :type partner_item_type: Optional[GetTeamsIntegrationMappingPartnerItemType], optional + :param partner_item_id: ID of the mapped item, + for which the mapping should be returned., defaults to None + :type partner_item_id: Optional[str], optional + :param box_item_id: Box item ID, for which the mappings should be returned., defaults to None + :type box_item_id: Optional[str], optional + :param box_item_type: Box item type, for + which the mappings should be returned., defaults to None + :type box_item_type: Optional[GetTeamsIntegrationMappingBoxItemType], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'partner_item_type': to_string(partner_item_type), + 'partner_item_id': to_string(partner_item_id), + 'box_item_id': to_string(box_item_id), + 'box_item_type': to_string(box_item_type), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/teams', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, IntegrationMappingsTeams) + + def create_teams_integration_mapping( + self, + partner_item: IntegrationMappingPartnerItemTeamsCreateRequest, + box_item: FolderReference, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> IntegrationMappingTeams: + """ + Creates a [Teams integration mapping](https://support.box.com/hc/en-us/articles/360044681474-Using-Box-for-Teams) + + by mapping a Teams channel to a Box item. + + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'partner_item': partner_item, 'box_item': box_item} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/teams', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, IntegrationMappingTeams) + + def update_teams_integration_mapping_by_id( + self, + integration_mapping_id: str, + *, + box_item: Optional[FolderReference] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> IntegrationMappingTeams: + """ + Updates a [Teams integration mapping](https://support.box.com/hc/en-us/articles/360044681474-Using-Box-for-Teams). + + Supports updating the Box folder ID and options. + + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param integration_mapping_id: An ID of an integration mapping. + Example: "11235432" + :type integration_mapping_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'box_item': box_item} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/teams/', + to_string(integration_mapping_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, IntegrationMappingTeams) + + def delete_teams_integration_mapping_by_id( + self, + integration_mapping_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a [Teams integration mapping](https://support.box.com/hc/en-us/articles/360044681474-Using-Box-for-Teams). + + You need Admin or Co-Admin role to + + + use this endpoint. + + :param integration_mapping_id: An ID of an integration mapping. + Example: "11235432" + :type integration_mapping_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/integration_mappings/teams/', + to_string(integration_mapping_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/invites.py b/box_sdk_gen/managers/invites.py new file mode 100644 index 000000000..e143df6fd --- /dev/null +++ b/box_sdk_gen/managers/invites.py @@ -0,0 +1,185 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.invite import Invite + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateInviteEnterprise(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of the enterprise. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class CreateInviteActionableBy(BaseObject): + def __init__(self, *, login: Optional[str] = None, **kwargs): + """ + :param login: The login of the invited user., defaults to None + :type login: Optional[str], optional + """ + super().__init__(**kwargs) + self.login = login + + +class InvitesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def create_invite( + self, + enterprise: CreateInviteEnterprise, + actionable_by: CreateInviteActionableBy, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Invite: + """ + Invites an existing external user to join an enterprise. + + The existing user can not be part of another enterprise and + + + must already have a Box account. Once invited, the user will receive an + + + email and are prompted to accept the invitation within the + + + Box web application. + + + This method requires the "Manage An Enterprise" scope enabled for + + + the application, which can be enabled within the developer console. + + :param enterprise: The enterprise to invite the user to. + :type enterprise: CreateInviteEnterprise + :param actionable_by: The user to invite. + :type actionable_by: CreateInviteActionableBy + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'enterprise': enterprise, 'actionable_by': actionable_by} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/invites']), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Invite) + + def get_invite_by_id( + self, + invite_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Invite: + """ + Returns the status of a user invite. + :param invite_id: The ID of an invite. + Example: "213723" + :type invite_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/invites/', + to_string(invite_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Invite) diff --git a/box_sdk_gen/managers/legal_hold_policies.py b/box_sdk_gen/managers/legal_hold_policies.py new file mode 100644 index 000000000..014b75ac3 --- /dev/null +++ b/box_sdk_gen/managers/legal_hold_policies.py @@ -0,0 +1,328 @@ +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.legal_hold_policies import LegalHoldPolicies + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.legal_hold_policy import LegalHoldPolicy + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class LegalHoldPoliciesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_legal_hold_policies( + self, + *, + policy_name: Optional[str] = None, + fields: Optional[List[str]] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> LegalHoldPolicies: + """ + Retrieves a list of legal hold policies that belong to + + an enterprise. + + :param policy_name: Limits results to policies for which the names start with + this search term. This is a case-insensitive prefix., defaults to None + :type policy_name: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'policy_name': to_string(policy_name), + 'fields': to_string(fields), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policies', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, LegalHoldPolicies) + + def create_legal_hold_policy( + self, + policy_name: str, + *, + description: Optional[str] = None, + filter_started_at: Optional[DateTime] = None, + filter_ended_at: Optional[DateTime] = None, + is_ongoing: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> LegalHoldPolicy: + """ + Create a new legal hold policy. + :param policy_name: The name of the policy. + :type policy_name: str + :param description: A description for the policy., defaults to None + :type description: Optional[str], optional + :param filter_started_at: The filter start date. + + When this policy is applied using a `custodian` legal + hold assignments, it will only apply to file versions + created or uploaded inside of the + date range. Other assignment types, such as folders and + files, will ignore the date filter. + + Required if `is_ongoing` is set to `false`., defaults to None + :type filter_started_at: Optional[DateTime], optional + :param filter_ended_at: The filter end date. + + When this policy is applied using a `custodian` legal + hold assignments, it will only apply to file versions + created or uploaded inside of the + date range. Other assignment types, such as folders and + files, will ignore the date filter. + + Required if `is_ongoing` is set to `false`., defaults to None + :type filter_ended_at: Optional[DateTime], optional + :param is_ongoing: Whether new assignments under this policy should + continue applying to files even after initialization. + + When this policy is applied using a legal hold assignment, + it will continue applying the policy to any new file versions + even after it has been applied. + + For example, if a legal hold assignment is placed on a user + today, and that user uploads a file tomorrow, that file will + get held. This will continue until the policy is retired. + + Required if no filter dates are set., defaults to None + :type is_ongoing: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'policy_name': policy_name, + 'description': description, + 'filter_started_at': filter_started_at, + 'filter_ended_at': filter_ended_at, + 'is_ongoing': is_ongoing, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policies', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, LegalHoldPolicy) + + def get_legal_hold_policy_by_id( + self, + legal_hold_policy_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> LegalHoldPolicy: + """ + Retrieve a legal hold policy. + :param legal_hold_policy_id: The ID of the legal hold policy. + Example: "324432" + :type legal_hold_policy_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policies/', + to_string(legal_hold_policy_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, LegalHoldPolicy) + + def update_legal_hold_policy_by_id( + self, + legal_hold_policy_id: str, + *, + policy_name: Optional[str] = None, + description: Optional[str] = None, + release_notes: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> LegalHoldPolicy: + """ + Update legal hold policy. + :param legal_hold_policy_id: The ID of the legal hold policy. + Example: "324432" + :type legal_hold_policy_id: str + :param policy_name: The name of the policy., defaults to None + :type policy_name: Optional[str], optional + :param description: A description for the policy., defaults to None + :type description: Optional[str], optional + :param release_notes: Notes around why the policy was released., defaults to None + :type release_notes: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'policy_name': policy_name, + 'description': description, + 'release_notes': release_notes, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policies/', + to_string(legal_hold_policy_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, LegalHoldPolicy) + + def delete_legal_hold_policy_by_id( + self, + legal_hold_policy_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Delete an existing legal hold policy. + + This is an asynchronous process. The policy will not be + + + fully deleted yet when the response returns. + + :param legal_hold_policy_id: The ID of the legal hold policy. + Example: "324432" + :type legal_hold_policy_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policies/', + to_string(legal_hold_policy_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/legal_hold_policy_assignments.py b/box_sdk_gen/managers/legal_hold_policy_assignments.py new file mode 100644 index 000000000..3e6812726 --- /dev/null +++ b/box_sdk_gen/managers/legal_hold_policy_assignments.py @@ -0,0 +1,393 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.legal_hold_policy_assignments import LegalHoldPolicyAssignments + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.legal_hold_policy_assignment import LegalHoldPolicyAssignment + +from box_sdk_gen.schemas.files_on_hold import FilesOnHold + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetLegalHoldPolicyAssignmentsAssignToType(str, Enum): + FILE = 'file' + FILE_VERSION = 'file_version' + FOLDER = 'folder' + USER = 'user' + OWNERSHIP = 'ownership' + INTERACTIONS = 'interactions' + + +class CreateLegalHoldPolicyAssignmentAssignToTypeField(str, Enum): + FILE = 'file' + FILE_VERSION = 'file_version' + FOLDER = 'folder' + USER = 'user' + OWNERSHIP = 'ownership' + INTERACTION = 'interaction' + + +class CreateLegalHoldPolicyAssignmentAssignTo(BaseObject): + _discriminator = 'type', { + 'file', + 'file_version', + 'folder', + 'user', + 'ownership', + 'interaction', + } + + def __init__( + self, type: CreateLegalHoldPolicyAssignmentAssignToTypeField, id: str, **kwargs + ): + """ + :param type: The type of item to assign the policy to. + :type type: CreateLegalHoldPolicyAssignmentAssignToTypeField + :param id: The ID of item to assign the policy to. + :type id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class LegalHoldPolicyAssignmentsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_legal_hold_policy_assignments( + self, + policy_id: str, + *, + assign_to_type: Optional[GetLegalHoldPolicyAssignmentsAssignToType] = None, + assign_to_id: Optional[str] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> LegalHoldPolicyAssignments: + """ + Retrieves a list of items a legal hold policy has been assigned to. + :param policy_id: The ID of the legal hold policy. + :type policy_id: str + :param assign_to_type: Filters the results by the type of item the + policy was applied to., defaults to None + :type assign_to_type: Optional[GetLegalHoldPolicyAssignmentsAssignToType], optional + :param assign_to_id: Filters the results by the ID of item the + policy was applied to., defaults to None + :type assign_to_id: Optional[str], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'policy_id': to_string(policy_id), + 'assign_to_type': to_string(assign_to_type), + 'assign_to_id': to_string(assign_to_id), + 'marker': to_string(marker), + 'limit': to_string(limit), + 'fields': to_string(fields), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policy_assignments', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, LegalHoldPolicyAssignments) + + def create_legal_hold_policy_assignment( + self, + policy_id: str, + assign_to: CreateLegalHoldPolicyAssignmentAssignTo, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> LegalHoldPolicyAssignment: + """ + Assign a legal hold to a file, file version, folder, or user. + :param policy_id: The ID of the policy to assign. + :type policy_id: str + :param assign_to: The item to assign the policy to. + :type assign_to: CreateLegalHoldPolicyAssignmentAssignTo + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'policy_id': policy_id, 'assign_to': assign_to} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policy_assignments', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, LegalHoldPolicyAssignment) + + def get_legal_hold_policy_assignment_by_id( + self, + legal_hold_policy_assignment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> LegalHoldPolicyAssignment: + """ + Retrieve a legal hold policy assignment. + :param legal_hold_policy_assignment_id: The ID of the legal hold policy assignment. + Example: "753465" + :type legal_hold_policy_assignment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policy_assignments/', + to_string(legal_hold_policy_assignment_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, LegalHoldPolicyAssignment) + + def delete_legal_hold_policy_assignment_by_id( + self, + legal_hold_policy_assignment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Remove a legal hold from an item. + + This is an asynchronous process. The policy will not be + + + fully removed yet when the response returns. + + :param legal_hold_policy_assignment_id: The ID of the legal hold policy assignment. + Example: "753465" + :type legal_hold_policy_assignment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policy_assignments/', + to_string(legal_hold_policy_assignment_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_legal_hold_policy_assignment_file_on_hold( + self, + legal_hold_policy_assignment_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FilesOnHold: + """ + Get a list of files with current file versions for a legal hold + + assignment. + + + In some cases you may want to get previous file versions instead. In these + + + cases, use the `GET /legal_hold_policy_assignments/:id/file_versions_on_hold` + + + API instead to return any previous versions of a file for this legal hold + + + policy assignment. + + + Due to ongoing re-architecture efforts this API might not return all file + + + versions held for this policy ID. Instead, this API will only return the + + + latest file version held in the newly developed architecture. The `GET + + + /file_version_legal_holds` API can be used to fetch current and past versions + + + of files held within the legacy architecture. + + + This endpoint does not support returning any content that is on hold due to + + + a Custodian collaborating on a Hub. + + + The `GET /legal_hold_policy_assignments?policy_id={id}` API can be used to + + + find a list of policy assignments for a given policy ID. + + :param legal_hold_policy_assignment_id: The ID of the legal hold policy assignment. + Example: "753465" + :type legal_hold_policy_assignment_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'marker': to_string(marker), + 'limit': to_string(limit), + 'fields': to_string(fields), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/legal_hold_policy_assignments/', + to_string(legal_hold_policy_assignment_id), + '/files_on_hold', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FilesOnHold) diff --git a/box_sdk_gen/managers/list_collaborations.py b/box_sdk_gen/managers/list_collaborations.py new file mode 100644 index 000000000..b30238ad5 --- /dev/null +++ b/box_sdk_gen/managers/list_collaborations.py @@ -0,0 +1,329 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.collaborations import Collaborations + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.collaborations_offset_paginated import ( + CollaborationsOffsetPaginated, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetCollaborationsStatus(str, Enum): + PENDING = 'pending' + + +class ListCollaborationsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_collaborations( + self, + file_id: str, + *, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Collaborations: + """ + Retrieves a list of pending and active collaborations for a + + file. This returns all the users that have access to the file + + + or have been invited to the file. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'limit': to_string(limit), + 'marker': to_string(marker), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/collaborations', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Collaborations) + + def get_folder_collaborations( + self, + folder_id: str, + *, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Collaborations: + """ + Retrieves a list of pending and active collaborations for a + + folder. This returns all the users that have access to the folder + + + or have been invited to the folder. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + Example: "12345" + :type folder_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'limit': to_string(limit), + 'marker': to_string(marker), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/collaborations', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Collaborations) + + def get_collaborations( + self, + status: GetCollaborationsStatus, + *, + fields: Optional[List[str]] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationsOffsetPaginated: + """ + Retrieves all pending collaboration invites for this user. + :param status: The status of the collaborations to retrieve. + :type status: GetCollaborationsStatus + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'status': to_string(status), + 'fields': to_string(fields), + 'offset': to_string(offset), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/collaborations'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationsOffsetPaginated) + + def get_group_collaborations( + self, + group_id: str, + *, + limit: Optional[int] = None, + offset: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> CollaborationsOffsetPaginated: + """ + Retrieves all the collaborations for a group. The user + + must have admin permissions to inspect enterprise's groups. + + + Each collaboration object has details on which files or + + + folders the group has access to and with what role. + + :param group_id: The ID of the group. + Example: "57645" + :type group_id: str + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'limit': to_string(limit), 'offset': to_string(offset)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/groups/', + to_string(group_id), + '/collaborations', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, CollaborationsOffsetPaginated) diff --git a/box_sdk_gen/managers/memberships.py b/box_sdk_gen/managers/memberships.py new file mode 100644 index 000000000..7719acbc6 --- /dev/null +++ b/box_sdk_gen/managers/memberships.py @@ -0,0 +1,444 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from typing import List + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.group_memberships import GroupMemberships + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.group_membership import GroupMembership + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateGroupMembershipUser(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of the user to add to the group. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class CreateGroupMembershipGroup(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of the group to add the user to. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class CreateGroupMembershipRole(str, Enum): + MEMBER = 'member' + ADMIN = 'admin' + + +class UpdateGroupMembershipByIdRole(str, Enum): + MEMBER = 'member' + ADMIN = 'admin' + + +class MembershipsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_user_memberships( + self, + user_id: str, + *, + limit: Optional[int] = None, + offset: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupMemberships: + """ + Retrieves all the groups for a user. Only members of this + + group or users with admin-level permissions will be able to + + + use this API. + + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'limit': to_string(limit), 'offset': to_string(offset)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/memberships', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupMemberships) + + def get_group_memberships( + self, + group_id: str, + *, + limit: Optional[int] = None, + offset: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupMemberships: + """ + Retrieves all the members for a group. Only members of this + + group or users with admin-level permissions will be able to + + + use this API. + + :param group_id: The ID of the group. + Example: "57645" + :type group_id: str + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'limit': to_string(limit), 'offset': to_string(offset)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/groups/', + to_string(group_id), + '/memberships', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupMemberships) + + def create_group_membership( + self, + user: CreateGroupMembershipUser, + group: CreateGroupMembershipGroup, + *, + role: Optional[CreateGroupMembershipRole] = None, + configurable_permissions: Union[Optional[Dict[str, bool]], NullValue] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupMembership: + """ + Creates a group membership. Only users with + + admin-level permissions will be able to use this API. + + :param user: The user to add to the group. + :type user: CreateGroupMembershipUser + :param group: The group to add the user to. + :type group: CreateGroupMembershipGroup + :param role: The role of the user in the group., defaults to None + :type role: Optional[CreateGroupMembershipRole], optional + :param configurable_permissions: Custom configuration for the permissions an admin + if a group will receive. This option has no effect + on members with a role of `member`. + + Setting these permissions overwrites the default + access levels of an admin. + + Specifying a value of `null` for this object will disable + all configurable permissions. Specifying permissions will set + them accordingly, omitted permissions will be enabled by default., defaults to None + :type configurable_permissions: Union[Optional[Dict[str, bool]], NullValue], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'user': user, + 'group': group, + 'role': role, + 'configurable_permissions': configurable_permissions, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/group_memberships'] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupMembership) + + def get_group_membership_by_id( + self, + group_membership_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupMembership: + """ + Retrieves a specific group membership. Only admins of this + + group or users with admin-level permissions will be able to + + + use this API. + + :param group_membership_id: The ID of the group membership. + Example: "434534" + :type group_membership_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/group_memberships/', + to_string(group_membership_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupMembership) + + def update_group_membership_by_id( + self, + group_membership_id: str, + *, + role: Optional[UpdateGroupMembershipByIdRole] = None, + configurable_permissions: Union[Optional[Dict[str, bool]], NullValue] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> GroupMembership: + """ + Updates a user's group membership. Only admins of this + + group or users with admin-level permissions will be able to + + + use this API. + + :param group_membership_id: The ID of the group membership. + Example: "434534" + :type group_membership_id: str + :param role: The role of the user in the group., defaults to None + :type role: Optional[UpdateGroupMembershipByIdRole], optional + :param configurable_permissions: Custom configuration for the permissions an admin + if a group will receive. This option has no effect + on members with a role of `member`. + + Setting these permissions overwrites the default + access levels of an admin. + + Specifying a value of `null` for this object will disable + all configurable permissions. Specifying permissions will set + them accordingly, omitted permissions will be enabled by default., defaults to None + :type configurable_permissions: Union[Optional[Dict[str, bool]], NullValue], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'role': role, + 'configurable_permissions': configurable_permissions, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/group_memberships/', + to_string(group_membership_id), + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, GroupMembership) + + def delete_group_membership_by_id( + self, + group_membership_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a specific group membership. Only admins of this + + group or users with admin-level permissions will be able to + + + use this API. + + :param group_membership_id: The ID of the group membership. + Example: "434534" + :type group_membership_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/group_memberships/', + to_string(group_membership_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/metadata_cascade_policies.py b/box_sdk_gen/managers/metadata_cascade_policies.py new file mode 100644 index 000000000..2d254c871 --- /dev/null +++ b/box_sdk_gen/managers/metadata_cascade_policies.py @@ -0,0 +1,333 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.metadata_cascade_policies import MetadataCascadePolicies + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.metadata_cascade_policy import MetadataCascadePolicy + +from box_sdk_gen.schemas.conflict_error import ConflictError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateMetadataCascadePolicyScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class ApplyMetadataCascadePolicyConflictResolution(str, Enum): + NONE = 'none' + OVERWRITE = 'overwrite' + + +class MetadataCascadePoliciesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_metadata_cascade_policies( + self, + folder_id: str, + *, + owner_enterprise_id: Optional[str] = None, + marker: Optional[str] = None, + offset: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataCascadePolicies: + """ + Retrieves a list of all the metadata cascade policies + + that are applied to a given folder. This can not be used on the root + + + folder with ID `0`. + + :param folder_id: Specifies which folder to return policies for. This can not be used on the + root folder with ID `0`. + :type folder_id: str + :param owner_enterprise_id: The ID of the enterprise ID for which to find metadata + cascade policies. If not specified, it defaults to the + current enterprise., defaults to None + :type owner_enterprise_id: Optional[str], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'folder_id': to_string(folder_id), + 'owner_enterprise_id': to_string(owner_enterprise_id), + 'marker': to_string(marker), + 'offset': to_string(offset), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_cascade_policies', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataCascadePolicies) + + def create_metadata_cascade_policy( + self, + folder_id: str, + scope: CreateMetadataCascadePolicyScope, + template_key: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataCascadePolicy: + """ + Creates a new metadata cascade policy that applies a given + + metadata template to a given folder and automatically + + + cascades it down to any files within that folder. + + + In order for the policy to be applied a metadata instance must first + + + be applied to the folder the policy is to be applied to. + + :param folder_id: The ID of the folder to apply the policy to. This folder will + need to already have an instance of the targeted metadata + template applied to it. + :type folder_id: str + :param scope: The scope of the targeted metadata template. This template will + need to already have an instance applied to the targeted folder. + :type scope: CreateMetadataCascadePolicyScope + :param template_key: The key of the targeted metadata template. This template will + need to already have an instance applied to the targeted folder. + + In many cases the template key is automatically derived + of its display name, for example `Contract Template` would + become `contractTemplate`. In some cases the creator of the + template will have provided its own template key. + + Please [list the templates for an enterprise][list], or + get all instances on a [file][file] or [folder][folder] + to inspect a template's key. + + [list]: e://get-metadata-templates-enterprise + [file]: e://get-files-id-metadata + [folder]: e://get-folders-id-metadata + :type template_key: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'folder_id': folder_id, + 'scope': scope, + 'templateKey': template_key, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_cascade_policies', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataCascadePolicy) + + def get_metadata_cascade_policy_by_id( + self, + metadata_cascade_policy_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataCascadePolicy: + """ + Retrieve a specific metadata cascade policy assigned to a folder. + :param metadata_cascade_policy_id: The ID of the metadata cascade policy. + Example: "6fd4ff89-8fc1-42cf-8b29-1890dedd26d7" + :type metadata_cascade_policy_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_cascade_policies/', + to_string(metadata_cascade_policy_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataCascadePolicy) + + def delete_metadata_cascade_policy_by_id( + self, + metadata_cascade_policy_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a metadata cascade policy. + :param metadata_cascade_policy_id: The ID of the metadata cascade policy. + Example: "6fd4ff89-8fc1-42cf-8b29-1890dedd26d7" + :type metadata_cascade_policy_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_cascade_policies/', + to_string(metadata_cascade_policy_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def apply_metadata_cascade_policy( + self, + metadata_cascade_policy_id: str, + conflict_resolution: ApplyMetadataCascadePolicyConflictResolution, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Force the metadata on a folder with a metadata cascade policy to be applied to + + all of its children. This can be used after creating a new cascade policy to + + + enforce the metadata to be cascaded down to all existing files within that + + + folder. + + :param metadata_cascade_policy_id: The ID of the cascade policy to force-apply. + Example: "6fd4ff89-8fc1-42cf-8b29-1890dedd26d7" + :type metadata_cascade_policy_id: str + :param conflict_resolution: Describes the desired behavior when dealing with the conflict + where a metadata template already has an instance applied + to a child. + + * `none` will preserve the existing value on the file + * `overwrite` will force-apply the templates values over + any existing values. + :type conflict_resolution: ApplyMetadataCascadePolicyConflictResolution + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'conflict_resolution': conflict_resolution} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_cascade_policies/', + to_string(metadata_cascade_policy_id), + '/apply', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/metadata_templates.py b/box_sdk_gen/managers/metadata_templates.py new file mode 100644 index 000000000..445c66a57 --- /dev/null +++ b/box_sdk_gen/managers/metadata_templates.py @@ -0,0 +1,636 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.metadata_templates import MetadataTemplates + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.metadata_template import MetadataTemplate + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetMetadataTemplateScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class UpdateMetadataTemplateScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class UpdateMetadataTemplateRequestBodyOpField(str, Enum): + EDITTEMPLATE = 'editTemplate' + ADDFIELD = 'addField' + REORDERFIELDS = 'reorderFields' + ADDENUMOPTION = 'addEnumOption' + REORDERENUMOPTIONS = 'reorderEnumOptions' + REORDERMULTISELECTOPTIONS = 'reorderMultiSelectOptions' + ADDMULTISELECTOPTION = 'addMultiSelectOption' + EDITFIELD = 'editField' + REMOVEFIELD = 'removeField' + EDITENUMOPTION = 'editEnumOption' + REMOVEENUMOPTION = 'removeEnumOption' + EDITMULTISELECTOPTION = 'editMultiSelectOption' + REMOVEMULTISELECTOPTION = 'removeMultiSelectOption' + + +class UpdateMetadataTemplateRequestBody(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'field_key': 'fieldKey', + 'field_keys': 'fieldKeys', + 'enum_option_key': 'enumOptionKey', + 'enum_option_keys': 'enumOptionKeys', + 'multi_select_option_key': 'multiSelectOptionKey', + 'multi_select_option_keys': 'multiSelectOptionKeys', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'fieldKey': 'field_key', + 'fieldKeys': 'field_keys', + 'enumOptionKey': 'enum_option_key', + 'enumOptionKeys': 'enum_option_keys', + 'multiSelectOptionKey': 'multi_select_option_key', + 'multiSelectOptionKeys': 'multi_select_option_keys', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + op: UpdateMetadataTemplateRequestBodyOpField, + *, + data: Optional[Dict] = None, + field_key: Optional[str] = None, + field_keys: Optional[List[str]] = None, + enum_option_key: Optional[str] = None, + enum_option_keys: Optional[List[str]] = None, + multi_select_option_key: Optional[str] = None, + multi_select_option_keys: Optional[List[str]] = None, + **kwargs + ): + """ + :param op: The type of change to perform on the template. Some + of these are hazardous as they will change existing templates. + :type op: UpdateMetadataTemplateRequestBodyOpField + :param data: The data for the operation. This will vary depending on the + operation being performed., defaults to None + :type data: Optional[Dict], optional + :param field_key: For operations that affect a single field this defines the key of + the field that is affected., defaults to None + :type field_key: Optional[str], optional + :param field_keys: For operations that affect multiple fields this defines the keys + of the fields that are affected., defaults to None + :type field_keys: Optional[List[str]], optional + :param enum_option_key: For operations that affect a single `enum` option this defines + the key of the option that is affected., defaults to None + :type enum_option_key: Optional[str], optional + :param enum_option_keys: For operations that affect multiple `enum` options this defines + the keys of the options that are affected., defaults to None + :type enum_option_keys: Optional[List[str]], optional + :param multi_select_option_key: For operations that affect a single multi select option this + defines the key of the option that is affected., defaults to None + :type multi_select_option_key: Optional[str], optional + :param multi_select_option_keys: For operations that affect multiple multi select options this + defines the keys of the options that are affected., defaults to None + :type multi_select_option_keys: Optional[List[str]], optional + """ + super().__init__(**kwargs) + self.op = op + self.data = data + self.field_key = field_key + self.field_keys = field_keys + self.enum_option_key = enum_option_key + self.enum_option_keys = enum_option_keys + self.multi_select_option_key = multi_select_option_key + self.multi_select_option_keys = multi_select_option_keys + + +class DeleteMetadataTemplateScope(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + + +class CreateMetadataTemplateFieldsTypeField(str, Enum): + STRING = 'string' + FLOAT = 'float' + DATE = 'date' + ENUM = 'enum' + MULTISELECT = 'multiSelect' + + +class CreateMetadataTemplateFieldsOptionsField(BaseObject): + def __init__(self, key: str, **kwargs): + """ + :param key: The text value of the option. This represents both the display name of the + option and the internal key used when updating templates. + :type key: str + """ + super().__init__(**kwargs) + self.key = key + + +class CreateMetadataTemplateFields(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'display_name': 'displayName', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'displayName': 'display_name', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'string', 'float', 'date', 'enum', 'multiSelect'} + + def __init__( + self, + type: CreateMetadataTemplateFieldsTypeField, + key: str, + display_name: str, + *, + description: Optional[str] = None, + hidden: Optional[bool] = None, + options: Optional[List[CreateMetadataTemplateFieldsOptionsField]] = None, + **kwargs + ): + """ + :param type: The type of field. The basic fields are a `string` field for text, a + `float` field for numbers, and a `date` fields to present the user with a + date-time picker. + + Additionally, metadata templates support an `enum` field for a basic list + of items, and ` multiSelect` field for a similar list of items where the + user can select more than one value. + :type type: CreateMetadataTemplateFieldsTypeField + :param key: A unique identifier for the field. The identifier must + be unique within the template to which it belongs. + :type key: str + :param display_name: The display name of the field as it is shown to the user in the web and + mobile apps. + :type display_name: str + :param description: A description of the field. This is not shown to the user., defaults to None + :type description: Optional[str], optional + :param hidden: Whether this field is hidden in the UI for the user and can only be set + through the API instead., defaults to None + :type hidden: Optional[bool], optional + :param options: A list of options for this field. This is used in combination with the + `enum` and `multiSelect` field types., defaults to None + :type options: Optional[List[CreateMetadataTemplateFieldsOptionsField]], optional + """ + super().__init__(**kwargs) + self.type = type + self.key = key + self.display_name = display_name + self.description = description + self.hidden = hidden + self.options = options + + +class MetadataTemplatesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_metadata_templates_by_instance_id( + self, + metadata_instance_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataTemplates: + """ + Finds a metadata template by searching for the ID of an instance of the + + template. + + :param metadata_instance_id: The ID of an instance of the metadata template to find. + :type metadata_instance_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'metadata_instance_id': to_string(metadata_instance_id), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/metadata_templates'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataTemplates) + + def get_metadata_template( + self, + scope: GetMetadataTemplateScope, + template_key: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataTemplate: + """ + Retrieves a metadata template by its `scope` and `templateKey` values. + + To find the `scope` and `templateKey` for a template, list all templates for + + + an enterprise or globally, or list all templates applied to a file or folder. + + :param scope: The scope of the metadata template. + Example: "global" + :type scope: GetMetadataTemplateScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/', + to_string(scope), + '/', + to_string(template_key), + '/schema', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataTemplate) + + def update_metadata_template( + self, + scope: UpdateMetadataTemplateScope, + template_key: str, + request_body: List[UpdateMetadataTemplateRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataTemplate: + """ + Updates a metadata template. + + The metadata template can only be updated if the template + + + already exists. + + + The update is applied atomically. If any errors occur during the + + + application of the operations, the metadata template will not be changed. + + :param scope: The scope of the metadata template. + Example: "global" + :type scope: UpdateMetadataTemplateScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param request_body: Request body of updateMetadataTemplate method + :type request_body: List[UpdateMetadataTemplateRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/', + to_string(scope), + '/', + to_string(template_key), + '/schema', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json-patch+json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataTemplate) + + def delete_metadata_template( + self, + scope: DeleteMetadataTemplateScope, + template_key: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Delete a metadata template and its instances. + + This deletion is permanent and can not be reversed. + + :param scope: The scope of the metadata template. + Example: "global" + :type scope: DeleteMetadataTemplateScope + :param template_key: The name of the metadata template. + Example: "properties" + :type template_key: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/', + to_string(scope), + '/', + to_string(template_key), + '/schema', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_metadata_template_by_id( + self, + template_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataTemplate: + """ + Retrieves a metadata template by its ID. + :param template_id: The ID of the template. + Example: "f7a9891f" + :type template_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/', + to_string(template_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataTemplate) + + def get_global_metadata_templates( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataTemplates: + """ + Used to retrieve all generic, global metadata templates available to all + + enterprises using Box. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/global', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataTemplates) + + def get_enterprise_metadata_templates( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataTemplates: + """ + Used to retrieve all metadata templates created to be used specifically within + + the user's enterprise. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/enterprise', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataTemplates) + + def create_metadata_template( + self, + scope: str, + display_name: str, + *, + template_key: Optional[str] = None, + hidden: Optional[bool] = None, + fields: Optional[List[CreateMetadataTemplateFields]] = None, + copy_instance_on_item_copy: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataTemplate: + """ + Creates a new metadata template that can be applied to + + files and folders. + + :param scope: The scope of the metadata template to create. Applications can + only create templates for use within the authenticated user's + enterprise. + + This value needs to be set to `enterprise`, as `global` scopes can + not be created by applications. + :type scope: str + :param display_name: The display name of the template. + :type display_name: str + :param template_key: A unique identifier for the template. This identifier needs to be + unique across the enterprise for which the metadata template is + being created. + + When not provided, the API will create a unique `templateKey` + based on the value of the `displayName`., defaults to None + :type template_key: Optional[str], optional + :param hidden: Defines if this template is visible in the Box web app UI, or if + it is purely intended for usage through the API., defaults to None + :type hidden: Optional[bool], optional + :param fields: An ordered list of template fields which are part of the template. + Each field can be a regular text field, date field, number field, + as well as a single or multi-select list., defaults to None + :type fields: Optional[List[CreateMetadataTemplateFields]], optional + :param copy_instance_on_item_copy: Whether or not to copy any metadata attached to a file or folder + when it is copied. By default, metadata is not copied along with a + file or folder when it is copied., defaults to None + :type copy_instance_on_item_copy: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'scope': scope, + 'templateKey': template_key, + 'displayName': display_name, + 'hidden': hidden, + 'fields': fields, + 'copyInstanceOnItemCopy': copy_instance_on_item_copy, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_templates/schema', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataTemplate) diff --git a/box_sdk_gen/managers/recent_items.py b/box_sdk_gen/managers/recent_items.py new file mode 100644 index 000000000..83313a980 --- /dev/null +++ b/box_sdk_gen/managers/recent_items.py @@ -0,0 +1,109 @@ +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.recent_items import RecentItems + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class RecentItemsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_recent_items( + self, + *, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RecentItems: + """ + Returns information about the recent items accessed + + by a user, either in the last 90 days or up to the last + + + 1000 items accessed. + + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'limit': to_string(limit), + 'marker': to_string(marker), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/recent_items'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RecentItems) diff --git a/box_sdk_gen/managers/retention_policies.py b/box_sdk_gen/managers/retention_policies.py new file mode 100644 index 000000000..9f5f5f723 --- /dev/null +++ b/box_sdk_gen/managers/retention_policies.py @@ -0,0 +1,443 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.retention_policies import RetentionPolicies + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.retention_policy import RetentionPolicy + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetRetentionPoliciesPolicyType(str, Enum): + FINITE = 'finite' + INDEFINITE = 'indefinite' + + +class CreateRetentionPolicyPolicyType(str, Enum): + FINITE = 'finite' + INDEFINITE = 'indefinite' + + +class CreateRetentionPolicyDispositionAction(str, Enum): + PERMANENTLY_DELETE = 'permanently_delete' + REMOVE_RETENTION = 'remove_retention' + + +class CreateRetentionPolicyRetentionType(str, Enum): + MODIFIABLE = 'modifiable' + NON_MODIFIABLE = 'non_modifiable' + + +class RetentionPoliciesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_retention_policies( + self, + *, + policy_name: Optional[str] = None, + policy_type: Optional[GetRetentionPoliciesPolicyType] = None, + created_by_user_id: Optional[str] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RetentionPolicies: + """ + Retrieves all of the retention policies for an enterprise. + :param policy_name: Filters results by a case sensitive prefix of the name of + retention policies., defaults to None + :type policy_name: Optional[str], optional + :param policy_type: Filters results by the type of retention policy., defaults to None + :type policy_type: Optional[GetRetentionPoliciesPolicyType], optional + :param created_by_user_id: Filters results by the ID of the user who created policy., defaults to None + :type created_by_user_id: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'policy_name': to_string(policy_name), + 'policy_type': to_string(policy_type), + 'created_by_user_id': to_string(created_by_user_id), + 'fields': to_string(fields), + 'limit': to_string(limit), + 'marker': to_string(marker), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/retention_policies'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RetentionPolicies) + + def create_retention_policy( + self, + policy_name: str, + policy_type: CreateRetentionPolicyPolicyType, + disposition_action: CreateRetentionPolicyDispositionAction, + *, + description: Optional[str] = None, + retention_length: Optional[str] = None, + retention_type: Optional[CreateRetentionPolicyRetentionType] = None, + can_owner_extend_retention: Optional[bool] = None, + are_owners_notified: Optional[bool] = None, + custom_notification_recipients: Optional[List[UserMini]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RetentionPolicy: + """ + Creates a retention policy. + :param policy_name: The name for the retention policy. + :type policy_name: str + :param policy_type: The type of the retention policy. A retention + policy type can either be `finite`, where a + specific amount of time to retain the content is known + upfront, or `indefinite`, where the amount of time + to retain the content is still unknown. + :type policy_type: CreateRetentionPolicyPolicyType + :param disposition_action: The disposition action of the retention policy. + `permanently_delete` deletes the content + retained by the policy permanently. + `remove_retention` lifts retention policy + from the content, allowing it to be deleted + by users once the retention policy has expired. + :type disposition_action: CreateRetentionPolicyDispositionAction + :param description: The additional text description of the retention policy., defaults to None + :type description: Optional[str], optional + :param retention_length: The length of the retention policy. This value + specifies the duration in days that the retention + policy will be active for after being assigned to + content. If the policy has a `policy_type` of + `indefinite`, the `retention_length` will also be + `indefinite`., defaults to None + :type retention_length: Optional[str], optional + :param retention_type: Specifies the retention type: + + * `modifiable`: You can modify the retention policy. For example, + you can add or remove folders, shorten or lengthen + the policy duration, or delete the assignment. + Use this type if your retention policy + is not related to any regulatory purposes. + + * `non_modifiable`: You can modify the retention policy + only in a limited way: add a folder, lengthen the duration, + retire the policy, change the disposition action + or notification settings. You cannot perform other actions, + such as deleting the assignment or shortening the + policy duration. Use this type to ensure + compliance with regulatory retention policies., defaults to None + :type retention_type: Optional[CreateRetentionPolicyRetentionType], optional + :param can_owner_extend_retention: Whether the owner of a file will be allowed to + extend the retention., defaults to None + :type can_owner_extend_retention: Optional[bool], optional + :param are_owners_notified: Whether owner and co-owners of a file are notified + when the policy nears expiration., defaults to None + :type are_owners_notified: Optional[bool], optional + :param custom_notification_recipients: A list of users notified when + the retention policy duration is about to end., defaults to None + :type custom_notification_recipients: Optional[List[UserMini]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'policy_name': policy_name, + 'description': description, + 'policy_type': policy_type, + 'disposition_action': disposition_action, + 'retention_length': retention_length, + 'retention_type': retention_type, + 'can_owner_extend_retention': can_owner_extend_retention, + 'are_owners_notified': are_owners_notified, + 'custom_notification_recipients': custom_notification_recipients, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/retention_policies'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RetentionPolicy) + + def get_retention_policy_by_id( + self, + retention_policy_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RetentionPolicy: + """ + Retrieves a retention policy. + :param retention_policy_id: The ID of the retention policy. + Example: "982312" + :type retention_policy_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policies/', + to_string(retention_policy_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RetentionPolicy) + + def update_retention_policy_by_id( + self, + retention_policy_id: str, + *, + policy_name: Union[Optional[str], NullValue] = None, + description: Union[Optional[str], NullValue] = None, + disposition_action: Optional[str] = None, + retention_type: Union[Optional[str], NullValue] = None, + retention_length: Optional[str] = None, + status: Union[Optional[str], NullValue] = None, + can_owner_extend_retention: Union[Optional[bool], NullValue] = None, + are_owners_notified: Union[Optional[bool], NullValue] = None, + custom_notification_recipients: Union[ + Optional[List[UserBase]], NullValue + ] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RetentionPolicy: + """ + Updates a retention policy. + :param retention_policy_id: The ID of the retention policy. + Example: "982312" + :type retention_policy_id: str + :param policy_name: The name for the retention policy., defaults to None + :type policy_name: Union[Optional[str], NullValue], optional + :param description: The additional text description of the retention policy., defaults to None + :type description: Union[Optional[str], NullValue], optional + :param disposition_action: The disposition action of the retention policy. + This action can be `permanently_delete`, which + will cause the content retained by the policy + to be permanently deleted, or `remove_retention`, + which will lift the retention policy from the content, + allowing it to be deleted by users, + once the retention policy has expired. + You can use `null` if you don't want to change `disposition_action`., defaults to None + :type disposition_action: Optional[str], optional + :param retention_type: Specifies the retention type: + + * `modifiable`: You can modify the retention policy. For example, + you can add or remove folders, shorten or lengthen + the policy duration, or delete the assignment. + Use this type if your retention policy + is not related to any regulatory purposes. + * `non-modifiable`: You can modify the retention policy + only in a limited way: add a folder, lengthen the duration, + retire the policy, change the disposition action + or notification settings. You cannot perform other actions, + such as deleting the assignment or shortening the + policy duration. Use this type to ensure + compliance with regulatory retention policies. + + When updating a retention policy, you can use + `non-modifiable` type only. You can convert a + `modifiable` policy to `non-modifiable`, but + not the other way around., defaults to None + :type retention_type: Union[Optional[str], NullValue], optional + :param retention_length: The length of the retention policy. This value + specifies the duration in days that the retention + policy will be active for after being assigned to + content. If the policy has a `policy_type` of + `indefinite`, the `retention_length` will also be + `indefinite`., defaults to None + :type retention_length: Optional[str], optional + :param status: Used to retire a retention policy. + + If not retiring a policy, do not include this parameter + or set it to `null`., defaults to None + :type status: Union[Optional[str], NullValue], optional + :param can_owner_extend_retention: Determines if the owner of items under the policy + can extend the retention when the original retention + duration is about to end., defaults to None + :type can_owner_extend_retention: Union[Optional[bool], NullValue], optional + :param are_owners_notified: Determines if owners and co-owners of items + under the policy are notified when + the retention duration is about to end., defaults to None + :type are_owners_notified: Union[Optional[bool], NullValue], optional + :param custom_notification_recipients: A list of users notified when the retention duration is about to end., defaults to None + :type custom_notification_recipients: Union[Optional[List[UserBase]], NullValue], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'policy_name': policy_name, + 'description': description, + 'disposition_action': disposition_action, + 'retention_type': retention_type, + 'retention_length': retention_length, + 'status': status, + 'can_owner_extend_retention': can_owner_extend_retention, + 'are_owners_notified': are_owners_notified, + 'custom_notification_recipients': custom_notification_recipients, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policies/', + to_string(retention_policy_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RetentionPolicy) + + def delete_retention_policy_by_id( + self, + retention_policy_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Permanently deletes a retention policy. + :param retention_policy_id: The ID of the retention policy. + Example: "982312" + :type retention_policy_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policies/', + to_string(retention_policy_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/retention_policy_assignments.py b/box_sdk_gen/managers/retention_policy_assignments.py new file mode 100644 index 000000000..fd612d1ec --- /dev/null +++ b/box_sdk_gen/managers/retention_policy_assignments.py @@ -0,0 +1,371 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.retention_policy_assignments import RetentionPolicyAssignments + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.retention_policy_assignment import RetentionPolicyAssignment + +from box_sdk_gen.schemas.files_under_retention import FilesUnderRetention + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetRetentionPolicyAssignmentsType(str, Enum): + FOLDER = 'folder' + ENTERPRISE = 'enterprise' + METADATA_TEMPLATE = 'metadata_template' + + +class CreateRetentionPolicyAssignmentAssignToTypeField(str, Enum): + ENTERPRISE = 'enterprise' + FOLDER = 'folder' + METADATA_TEMPLATE = 'metadata_template' + + +class CreateRetentionPolicyAssignmentAssignTo(BaseObject): + _discriminator = 'type', {'enterprise', 'folder', 'metadata_template'} + + def __init__( + self, + type: CreateRetentionPolicyAssignmentAssignToTypeField, + *, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of item to assign the policy to. + :type type: CreateRetentionPolicyAssignmentAssignToTypeField + :param id: The ID of item to assign the policy to. + Set to `null` or omit when `type` is set to + `enterprise`., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class CreateRetentionPolicyAssignmentFilterFields(BaseObject): + def __init__( + self, *, field: Optional[str] = None, value: Optional[str] = None, **kwargs + ): + """ + :param field: The metadata attribute key id., defaults to None + :type field: Optional[str], optional + :param value: The metadata attribute field id. For value, only + enum and multiselect types are supported., defaults to None + :type value: Optional[str], optional + """ + super().__init__(**kwargs) + self.field = field + self.value = value + + +class RetentionPolicyAssignmentsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_retention_policy_assignments( + self, + retention_policy_id: str, + *, + type: Optional[GetRetentionPolicyAssignmentsType] = None, + fields: Optional[List[str]] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RetentionPolicyAssignments: + """ + Returns a list of all retention policy assignments associated with a specified + + retention policy. + + :param retention_policy_id: The ID of the retention policy. + Example: "982312" + :type retention_policy_id: str + :param type: The type of the retention policy assignment to retrieve., defaults to None + :type type: Optional[GetRetentionPolicyAssignmentsType], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'type': to_string(type), + 'fields': to_string(fields), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policies/', + to_string(retention_policy_id), + '/assignments', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RetentionPolicyAssignments) + + def create_retention_policy_assignment( + self, + policy_id: str, + assign_to: CreateRetentionPolicyAssignmentAssignTo, + *, + filter_fields: Optional[ + List[CreateRetentionPolicyAssignmentFilterFields] + ] = None, + start_date_field: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RetentionPolicyAssignment: + """ + Assigns a retention policy to an item. + :param policy_id: The ID of the retention policy to assign. + :type policy_id: str + :param assign_to: The item to assign the policy to. + :type assign_to: CreateRetentionPolicyAssignmentAssignTo + :param filter_fields: If the `assign_to` type is `metadata_template`, + then optionally add the `filter_fields` parameter which will + require an array of objects with a field entry and a value entry. + Currently only one object of `field` and `value` is supported., defaults to None + :type filter_fields: Optional[List[CreateRetentionPolicyAssignmentFilterFields]], optional + :param start_date_field: The date the retention policy assignment begins. + + If the `assigned_to` type is `metadata_template`, + this field can be a date field's metadata attribute key id., defaults to None + :type start_date_field: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'policy_id': policy_id, + 'assign_to': assign_to, + 'filter_fields': filter_fields, + 'start_date_field': start_date_field, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policy_assignments', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RetentionPolicyAssignment) + + def get_retention_policy_assignment_by_id( + self, + retention_policy_assignment_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> RetentionPolicyAssignment: + """ + Retrieves a retention policy assignment. + :param retention_policy_assignment_id: The ID of the retention policy assignment. + Example: "1233123" + :type retention_policy_assignment_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policy_assignments/', + to_string(retention_policy_assignment_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, RetentionPolicyAssignment) + + def delete_retention_policy_assignment_by_id( + self, + retention_policy_assignment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes a retention policy assignment + + applied to content. + + :param retention_policy_assignment_id: The ID of the retention policy assignment. + Example: "1233123" + :type retention_policy_assignment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policy_assignments/', + to_string(retention_policy_assignment_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_files_under_retention_policy_assignment( + self, + retention_policy_assignment_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FilesUnderRetention: + """ + Returns a list of files under retention for a retention policy assignment. + :param retention_policy_assignment_id: The ID of the retention policy assignment. + Example: "1233123" + :type retention_policy_assignment_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/retention_policy_assignments/', + to_string(retention_policy_assignment_id), + '/files_under_retention', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FilesUnderRetention) diff --git a/box_sdk_gen/managers/search.py b/box_sdk_gen/managers/search.py new file mode 100644 index 000000000..f5adde965 --- /dev/null +++ b/box_sdk_gen/managers/search.py @@ -0,0 +1,558 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from typing import List + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.search_results import SearchResults + +from box_sdk_gen.schemas.search_results_with_shared_links import ( + SearchResultsWithSharedLinks, +) + +from box_sdk_gen.schemas.metadata_query_results import MetadataQueryResults + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.metadata_query import MetadataQuery + +from box_sdk_gen.schemas.search_results_response import SearchResultsResponse + +from box_sdk_gen.schemas.metadata_filter import MetadataFilter + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.serialization.json import sd_to_json + + +class SearchByMetadataQueryOrderByDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class SearchByMetadataQueryOrderBy(BaseObject): + def __init__( + self, + *, + field_key: Optional[str] = None, + direction: Optional[SearchByMetadataQueryOrderByDirectionField] = None, + **kwargs + ): + """ + :param field_key: The metadata template field to order by. + + The `field_key` represents the `key` value of a field from the + metadata template being searched for., defaults to None + :type field_key: Optional[str], optional + :param direction: The direction to order by, either ascending or descending. + + The `ordering` direction must be the same for each item in the + array., defaults to None + :type direction: Optional[SearchByMetadataQueryOrderByDirectionField], optional + """ + super().__init__(**kwargs) + self.field_key = field_key + self.direction = direction + + +class SearchForContentScope(str, Enum): + USER_CONTENT = 'user_content' + ENTERPRISE_CONTENT = 'enterprise_content' + + +class SearchForContentContentTypes(str, Enum): + NAME = 'name' + DESCRIPTION = 'description' + FILE_CONTENT = 'file_content' + COMMENTS = 'comments' + TAG = 'tag' + + +class SearchForContentType(str, Enum): + FILE = 'file' + FOLDER = 'folder' + WEB_LINK = 'web_link' + + +class SearchForContentTrashContent(str, Enum): + NON_TRASHED_ONLY = 'non_trashed_only' + TRASHED_ONLY = 'trashed_only' + ALL_ITEMS = 'all_items' + + +class SearchForContentSort(str, Enum): + MODIFIED_AT = 'modified_at' + RELEVANCE = 'relevance' + + +class SearchForContentDirection(str, Enum): + DESC = 'DESC' + ASC = 'ASC' + + +class SearchManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def search_by_metadata_query( + self, + from_: str, + ancestor_folder_id: str, + *, + query: Optional[str] = None, + query_params: Optional[Dict] = None, + order_by: Optional[List[SearchByMetadataQueryOrderBy]] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> MetadataQueryResults: + """ + Create a search using SQL-like syntax to return items that match specific + + metadata. + + + By default, this endpoint returns only the most basic info about the items for + + + which the query matches. To get additional fields for each item, including any + + + of the metadata, use the `fields` attribute in the query. + + :param from_: Specifies the template used in the query. Must be in the form + `scope.templateKey`. Not all templates can be used in this field, + most notably the built-in, Box-provided classification templates + can not be used in a query. + :type from_: str + :param ancestor_folder_id: The ID of the folder that you are restricting the query to. A + value of zero will return results from all folders you have access + to. A non-zero value will only return results found in the folder + corresponding to the ID or in any of its subfolders. + :type ancestor_folder_id: str + :param query: The query to perform. A query is a logical expression that is very similar + to a SQL `SELECT` statement. Values in the search query can be turned into + parameters specified in the `query_param` arguments list to prevent having + to manually insert search values into the query string. + + For example, a value of `:amount` would represent the `amount` value in + `query_params` object., defaults to None + :type query: Optional[str], optional + :param query_params: Set of arguments corresponding to the parameters specified in the + `query`. The type of each parameter used in the `query_params` must match + the type of the corresponding metadata template field., defaults to None + :type query_params: Optional[Dict], optional + :param order_by: A list of template fields and directions to sort the metadata query + results by. + + The ordering `direction` must be the same for each item in the array., defaults to None + :type order_by: Optional[List[SearchByMetadataQueryOrderBy]], optional + :param limit: A value between 0 and 100 that indicates the maximum number of results + to return for a single request. This only specifies a maximum + boundary and will not guarantee the minimum number of results + returned., defaults to None + :type limit: Optional[int], optional + :param marker: Marker to use for requesting the next page., defaults to None + :type marker: Optional[str], optional + :param fields: By default, this endpoint returns only the most basic info about the items for + which the query matches. This attribute can be used to specify a list of + additional attributes to return for any item, including its metadata. + + This attribute takes a list of item fields, metadata template identifiers, + or metadata template field identifiers. + + For example: + + * `created_by` will add the details of the user who created the item to + the response. + * `metadata..` will return the mini-representation + of the metadata instance identified by the `scope` and `templateKey`. + * `metadata...` will return all the mini-representation + of the metadata instance identified by the `scope` and `templateKey` plus + the field specified by the `field` name. Multiple fields for the same + `scope` and `templateKey` can be defined., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'from': from_, + 'query': query, + 'query_params': query_params, + 'ancestor_folder_id': ancestor_folder_id, + 'order_by': order_by, + 'limit': limit, + 'marker': marker, + 'fields': fields, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/metadata_queries/execute_read', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, MetadataQueryResults) + + def search_for_content( + self, + *, + query: Optional[str] = None, + scope: Optional[SearchForContentScope] = None, + file_extensions: Optional[List[str]] = None, + created_at_range: Optional[List[str]] = None, + updated_at_range: Optional[List[str]] = None, + size_range: Optional[List[int]] = None, + owner_user_ids: Optional[List[str]] = None, + recent_updater_user_ids: Optional[List[str]] = None, + ancestor_folder_ids: Optional[List[str]] = None, + content_types: Optional[List[SearchForContentContentTypes]] = None, + type: Optional[SearchForContentType] = None, + trash_content: Optional[SearchForContentTrashContent] = None, + mdfilters: Optional[List[MetadataFilter]] = None, + sort: Optional[SearchForContentSort] = None, + direction: Optional[SearchForContentDirection] = None, + limit: Optional[int] = None, + include_recent_shared_links: Optional[bool] = None, + fields: Optional[List[str]] = None, + offset: Optional[int] = None, + deleted_user_ids: Optional[List[str]] = None, + deleted_at_range: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SearchResultsResponse: + """ + Searches for files, folders, web links, and shared files across the + + users content or across the entire enterprise. + + :param query: The string to search for. This query is matched against item names, + descriptions, text content of files, and various other fields of + the different item types. + + This parameter supports a variety of operators to further refine + the results returns. + + * `""` - by wrapping a query in double quotes only exact matches are + returned by the API. Exact searches do not return search matches + based on specific character sequences. Instead, they return + matches based on phrases, that is, word sequences. For example: + A search for `"Blue-Box"` may return search results including + the sequence `"blue.box"`, `"Blue Box"`, and `"Blue-Box"`; + any item containing the words `Blue` and `Box` consecutively, in + the order specified. + * `AND` - returns items that contain both the search terms. For + example, a search for `marketing AND BoxWorks` returns items + that have both `marketing` and `BoxWorks` within its text in any order. + It does not return a result that only has `BoxWorks` in its text. + * `OR` - returns items that contain either of the search terms. For + example, a search for `marketing OR BoxWorks` returns a result that + has either `marketing` or `BoxWorks` within its text. Using this + operator is not necessary as we implicitly interpret multi-word + queries as `OR` unless another supported boolean term is used. + * `NOT` - returns items that do not contain the search term provided. + For example, a search for `marketing AND NOT BoxWorks` returns a result + that has only `marketing` within its text. Results containing + `BoxWorks` are omitted. + + We do not support lower case (that is, + `and`, `or`, and `not`) or mixed case (that is, `And`, `Or`, and `Not`) + operators. + + This field is required unless the `mdfilters` parameter is defined., defaults to None + :type query: Optional[str], optional + :param scope: Limits the search results to either the files that the user has + access to, or to files available to the entire enterprise. + + The scope defaults to `user_content`, which limits the search + results to content that is available to the currently authenticated + user. + + The `enterprise_content` can be requested by an admin through our + support channels. Once this scope has been enabled for a user, it + will allow that use to query for content across the entire + enterprise and not only the content that they have access to., defaults to None + :type scope: Optional[SearchForContentScope], optional + :param file_extensions: Limits the search results to any files that match any of the provided + file extensions. This list is a comma-separated list of file extensions + without the dots., defaults to None + :type file_extensions: Optional[List[str]], optional + :param created_at_range: Limits the search results to any items created within + a given date range. + + Date ranges are defined as comma separated RFC3339 + timestamps. + + If the start date is omitted (`,2014-05-17T13:35:01-07:00`) + anything created before the end date will be returned. + + If the end date is omitted (`2014-05-15T13:35:01-07:00,`) the + current date will be used as the end date instead., defaults to None + :type created_at_range: Optional[List[str]], optional + :param updated_at_range: Limits the search results to any items updated within + a given date range. + + Date ranges are defined as comma separated RFC3339 + timestamps. + + If the start date is omitted (`,2014-05-17T13:35:01-07:00`) + anything updated before the end date will be returned. + + If the end date is omitted (`2014-05-15T13:35:01-07:00,`) the + current date will be used as the end date instead., defaults to None + :type updated_at_range: Optional[List[str]], optional + :param size_range: Limits the search results to any items with a size within + a given file size range. This applied to files and folders. + + Size ranges are defined as comma separated list of a lower + and upper byte size limit (inclusive). + + The upper and lower bound can be omitted to create open ranges., defaults to None + :type size_range: Optional[List[int]], optional + :param owner_user_ids: Limits the search results to any items that are owned + by the given list of owners, defined as a list of comma separated + user IDs. + + The items still need to be owned or shared with + the currently authenticated user for them to show up in the search + results. If the user does not have access to any files owned by any of + the users an empty result set will be returned. + + To search across an entire enterprise, we recommend using the + `enterprise_content` scope parameter which can be requested with our + support team., defaults to None + :type owner_user_ids: Optional[List[str]], optional + :param recent_updater_user_ids: Limits the search results to any items that have been updated + by the given list of users, defined as a list of comma separated + user IDs. + + The items still need to be owned or shared with + the currently authenticated user for them to show up in the search + results. If the user does not have access to any files owned by any of + the users an empty result set will be returned. + + This feature only searches back to the last 10 versions of an item., defaults to None + :type recent_updater_user_ids: Optional[List[str]], optional + :param ancestor_folder_ids: Limits the search results to items within the given + list of folders, defined as a comma separated lists + of folder IDs. + + Search results will also include items within any subfolders + of those ancestor folders. + + The folders still need to be owned or shared with + the currently authenticated user. If the folder is not accessible by this + user, or it does not exist, a `HTTP 404` error code will be returned + instead. + + To search across an entire enterprise, we recommend using the + `enterprise_content` scope parameter which can be requested with our + support team., defaults to None + :type ancestor_folder_ids: Optional[List[str]], optional + :param content_types: Limits the search results to any items that match the search query + for a specific part of the file, for example the file description. + + Content types are defined as a comma separated lists + of Box recognized content types. The allowed content types are as follows. + + * `name` - The name of the item, as defined by its `name` field. + * `description` - The description of the item, as defined by its + `description` field. + * `file_content` - The actual content of the file. + * `comments` - The content of any of the comments on a file or + folder. + * `tags` - Any tags that are applied to an item, as defined by its + `tags` field., defaults to None + :type content_types: Optional[List[SearchForContentContentTypes]], optional + :param type: Limits the search results to any items of this type. This + parameter only takes one value. By default the API returns + items that match any of these types. + + * `file` - Limits the search results to files, + * `folder` - Limits the search results to folders, + * `web_link` - Limits the search results to web links, also known + as bookmarks., defaults to None + :type type: Optional[SearchForContentType], optional + :param trash_content: Determines if the search should look in the trash for items. + + By default, this API only returns search results for items + not currently in the trash (`non_trashed_only`). + + * `trashed_only` - Only searches for items currently in the trash + * `non_trashed_only` - Only searches for items currently not in + the trash + * `all_items` - Searches for both trashed and non-trashed items., defaults to None + :type trash_content: Optional[SearchForContentTrashContent], optional + :param mdfilters: Limits the search results to any items for which the metadata matches the provided filter. + This parameter is a list that specifies exactly **one** metadata template used to filter the search results. + The parameter is required unless the `query` parameter is provided., defaults to None + :type mdfilters: Optional[List[MetadataFilter]], optional + :param sort: Defines the order in which search results are returned. This API + defaults to returning items by relevance unless this parameter is + explicitly specified. + + * `relevance` (default) returns the results sorted by relevance to the + query search term. The relevance is based on the occurrence of the search + term in the items name, description, content, and additional properties. + * `modified_at` returns the results ordered in descending order by date + at which the item was last modified., defaults to None + :type sort: Optional[SearchForContentSort], optional + :param direction: Defines the direction in which search results are ordered. This API + defaults to returning items in descending (`DESC`) order unless this + parameter is explicitly specified. + + When results are sorted by `relevance` the ordering is locked to returning + items in descending order of relevance, and this parameter is ignored., defaults to None + :type direction: Optional[SearchForContentDirection], optional + :param limit: Defines the maximum number of items to return as part of a page of + results., defaults to None + :type limit: Optional[int], optional + :param include_recent_shared_links: Defines whether the search results should include any items + that the user recently accessed through a shared link. + + When this parameter has been set to true, + the format of the response of this API changes to return + a list of [Search Results with + Shared Links](r://search_results_with_shared_links)., defaults to None + :type include_recent_shared_links: Optional[bool], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param deleted_user_ids: Limits the search results to items that were deleted by the given + list of users, defined as a list of comma separated user IDs. + + The `trash_content` parameter needs to be set to `trashed_only`. + + If searching in trash is not performed, an empty result set + is returned. The items need to be owned or shared with + the currently authenticated user for them to show up in the search + results. + + If the user does not have access to any files owned by + any of the users, an empty result set is returned. + + Data available from 2023-02-01 onwards., defaults to None + :type deleted_user_ids: Optional[List[str]], optional + :param deleted_at_range: Limits the search results to any items deleted within a given + date range. + + Date ranges are defined as comma separated RFC3339 timestamps. + + If the start date is omitted (`2014-05-17T13:35:01-07:00`), + anything deleted before the end date will be returned. + + If the end date is omitted (`2014-05-15T13:35:01-07:00`), + the current date will be used as the end date instead. + + The `trash_content` parameter needs to be set to `trashed_only`. + + If searching in trash is not performed, then an empty result + is returned. + + Data available from 2023-02-01 onwards., defaults to None + :type deleted_at_range: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'query': to_string(query), + 'scope': to_string(scope), + 'file_extensions': to_string(file_extensions), + 'created_at_range': to_string(created_at_range), + 'updated_at_range': to_string(updated_at_range), + 'size_range': to_string(size_range), + 'owner_user_ids': to_string(owner_user_ids), + 'recent_updater_user_ids': to_string(recent_updater_user_ids), + 'ancestor_folder_ids': to_string(ancestor_folder_ids), + 'content_types': to_string(content_types), + 'type': to_string(type), + 'trash_content': to_string(trash_content), + 'mdfilters': to_string(mdfilters), + 'sort': to_string(sort), + 'direction': to_string(direction), + 'limit': to_string(limit), + 'include_recent_shared_links': to_string(include_recent_shared_links), + 'fields': to_string(fields), + 'offset': to_string(offset), + 'deleted_user_ids': to_string(deleted_user_ids), + 'deleted_at_range': to_string(deleted_at_range), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/search']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SearchResultsResponse) diff --git a/box_sdk_gen/managers/session_termination.py b/box_sdk_gen/managers/session_termination.py new file mode 100644 index 000000000..756666a0c --- /dev/null +++ b/box_sdk_gen/managers/session_termination.py @@ -0,0 +1,139 @@ +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.session_termination_message import SessionTerminationMessage + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + + +class SessionTerminationManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def terminate_users_sessions( + self, + user_ids: List[str], + user_logins: List[str], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SessionTerminationMessage: + """ + Validates the roles and permissions of the user, + + and creates asynchronous jobs + + + to terminate the user's sessions. + + + Returns the status for the POST request. + + :param user_ids: A list of user IDs. + :type user_ids: List[str] + :param user_logins: A list of user logins. + :type user_logins: List[str] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'user_ids': user_ids, 'user_logins': user_logins} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/terminate_sessions', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SessionTerminationMessage) + + def terminate_groups_sessions( + self, + group_ids: List[str], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SessionTerminationMessage: + """ + Validates the roles and permissions of the group, + + and creates asynchronous jobs + + + to terminate the group's sessions. + + + Returns the status for the POST request. + + :param group_ids: A list of group IDs. + :type group_ids: List[str] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'group_ids': group_ids} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/groups/terminate_sessions', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SessionTerminationMessage) diff --git a/box_sdk_gen/managers/shared_links_app_items.py b/box_sdk_gen/managers/shared_links_app_items.py new file mode 100644 index 000000000..bb9017660 --- /dev/null +++ b/box_sdk_gen/managers/shared_links_app_items.py @@ -0,0 +1,84 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.app_item import AppItem + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class SharedLinksAppItemsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def find_app_item_for_shared_link( + self, boxapi: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> AppItem: + """ + Returns the app item represented by a shared link. + + The link can originate from the current enterprise or another. + + :param boxapi: A header containing the shared link and optional password for the + shared link. + + The format for this header is `shared_link=[link]&shared_link_password=[password]`. + :type boxapi: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'boxapi': to_string(boxapi), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shared_items#app_items', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, AppItem) diff --git a/box_sdk_gen/managers/shared_links_files.py b/box_sdk_gen/managers/shared_links_files.py new file mode 100644 index 000000000..0cfad2018 --- /dev/null +++ b/box_sdk_gen/managers/shared_links_files.py @@ -0,0 +1,540 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class AddShareLinkToFileSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class AddShareLinkToFileSharedLinkPermissionsField(BaseObject): + def __init__( + self, + *, + can_download: Optional[bool] = None, + can_preview: Optional[bool] = None, + can_edit: Optional[bool] = None, + **kwargs + ): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + :param can_preview: If the shared link allows for previewing of files. + This value is always `true`. For shared links on folders + this also applies to any items in the folder., defaults to None + :type can_preview: Optional[bool], optional + :param can_edit: If the shared link allows for editing of files. + This can only be set when `access` is set to + `open` or `company`. + This value can only be `true` is `can_download` is + also `true`., defaults to None + :type can_edit: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class AddShareLinkToFileSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[AddShareLinkToFileSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[AddShareLinkToFileSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the file (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[AddShareLinkToFileSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared + links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts. The value must be greater than the + current date and time., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class UpdateSharedLinkOnFileSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class UpdateSharedLinkOnFileSharedLinkPermissionsField(BaseObject): + def __init__( + self, + *, + can_download: Optional[bool] = None, + can_preview: Optional[bool] = None, + can_edit: Optional[bool] = None, + **kwargs + ): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + :param can_preview: If the shared link allows for previewing of files. + This value is always `true`. For shared links on folders + this also applies to any items in the folder., defaults to None + :type can_preview: Optional[bool], optional + :param can_edit: If the shared link allows for editing of files. + This can only be set when `access` is set to + `open` or `company`. + This value can only be `true` is `can_download` is + also `true`., defaults to None + :type can_edit: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class UpdateSharedLinkOnFileSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateSharedLinkOnFileSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[UpdateSharedLinkOnFileSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the folder (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[UpdateSharedLinkOnFileSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared + links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts. The value must be greater than the + current date and time., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class RemoveSharedLinkFromFileSharedLink(BaseObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class SharedLinksFilesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def find_file_for_shared_link( + self, + boxapi: str, + *, + fields: Optional[List[str]] = None, + if_none_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + """ + Returns the file represented by a shared link. + + A shared file can be represented by a shared link, + + + which can originate within the current enterprise or within another. + + + This endpoint allows an application to retrieve information about a + + + shared file when only given a shared link. + + + The `shared_link_permission_options` array field can be returned + + + by requesting it in the `fields` query parameter. + + :param boxapi: A header containing the shared link and optional password for the + shared link. + + The format for this header is as follows: + + `shared_link=[link]&shared_link_password=[password]`. + :type boxapi: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param if_none_match: Ensures an item is only returned if it has changed. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `304 Not Modified` if the item has not + changed since., defaults to None + :type if_none_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + { + 'if-none-match': to_string(if_none_match), + 'boxapi': to_string(boxapi), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/shared_items'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) + + def get_shared_link_for_file( + self, + file_id: str, + fields: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + """ + Gets the information for a shared link on a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '#get_shared_link', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) + + def add_share_link_to_file( + self, + file_id: str, + fields: str, + *, + shared_link: Optional[AddShareLinkToFileSharedLink] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + """ + Adds a shared link to a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: The settings for the shared link to create on the file. + Use an empty object (`{}`) to use the default settings for shared + links., defaults to None + :type shared_link: Optional[AddShareLinkToFileSharedLink], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '#add_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) + + def update_shared_link_on_file( + self, + file_id: str, + fields: str, + *, + shared_link: Optional[UpdateSharedLinkOnFileSharedLink] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + """ + Updates a shared link on a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: The settings for the shared link to update., defaults to None + :type shared_link: Optional[UpdateSharedLinkOnFileSharedLink], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '#update_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) + + def remove_shared_link_from_file( + self, + file_id: str, + fields: str, + *, + shared_link: Union[ + Optional[RemoveSharedLinkFromFileSharedLink], NullValue + ] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FileFull: + """ + Removes a shared link from a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: By setting this value to `null`, the shared link + is removed from the file., defaults to None + :type shared_link: Union[Optional[RemoveSharedLinkFromFileSharedLink], NullValue], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '#remove_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FileFull) diff --git a/box_sdk_gen/managers/shared_links_folders.py b/box_sdk_gen/managers/shared_links_folders.py new file mode 100644 index 000000000..94be61488 --- /dev/null +++ b/box_sdk_gen/managers/shared_links_folders.py @@ -0,0 +1,546 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class AddShareLinkToFolderSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class AddShareLinkToFolderSharedLinkPermissionsField(BaseObject): + def __init__( + self, + *, + can_download: Optional[bool] = None, + can_preview: Optional[bool] = None, + can_edit: Optional[bool] = None, + **kwargs + ): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + :param can_preview: If the shared link allows for previewing of files. + This value is always `true`. For shared links on folders + this also applies to any items in the folder., defaults to None + :type can_preview: Optional[bool], optional + :param can_edit: This value can only be `false` for items + with a `type` of `folder`., defaults to None + :type can_edit: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class AddShareLinkToFolderSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[AddShareLinkToFolderSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[AddShareLinkToFolderSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the folder (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[AddShareLinkToFolderSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared + links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts. The value must be greater than the + current date and time., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class UpdateSharedLinkOnFolderSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class UpdateSharedLinkOnFolderSharedLinkPermissionsField(BaseObject): + def __init__( + self, + *, + can_download: Optional[bool] = None, + can_preview: Optional[bool] = None, + can_edit: Optional[bool] = None, + **kwargs + ): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + :param can_preview: If the shared link allows for previewing of files. + This value is always `true`. For shared links on folders + this also applies to any items in the folder., defaults to None + :type can_preview: Optional[bool], optional + :param can_edit: This value can only be `false` for items + with a `type` of `folder`., defaults to None + :type can_edit: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class UpdateSharedLinkOnFolderSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateSharedLinkOnFolderSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[ + UpdateSharedLinkOnFolderSharedLinkPermissionsField + ] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the folder (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[UpdateSharedLinkOnFolderSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared + links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts. The value must be greater than the + current date and time., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class RemoveSharedLinkFromFolderSharedLink(BaseObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class SharedLinksFoldersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def find_folder_for_shared_link( + self, + boxapi: str, + *, + fields: Optional[List[str]] = None, + if_none_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + """ + Return the folder represented by a shared link. + + A shared folder can be represented by a shared link, + + + which can originate within the current enterprise or within another. + + + This endpoint allows an application to retrieve information about a + + + shared folder when only given a shared link. + + :param boxapi: A header containing the shared link and optional password for the + shared link. + + The format for this header is as follows: + + `shared_link=[link]&shared_link_password=[password]`. + :type boxapi: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param if_none_match: Ensures an item is only returned if it has changed. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `304 Not Modified` if the item has not + changed since., defaults to None + :type if_none_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + { + 'if-none-match': to_string(if_none_match), + 'boxapi': to_string(boxapi), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shared_items#folders', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) + + def get_shared_link_for_folder( + self, + folder_id: str, + fields: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + """ + Gets the information for a shared link on a folder. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '#get_shared_link', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) + + def add_share_link_to_folder( + self, + folder_id: str, + fields: str, + *, + shared_link: Optional[AddShareLinkToFolderSharedLink] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + """ + Adds a shared link to a folder. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: The settings for the shared link to create on the folder. + + Use an empty object (`{}`) to use the default settings for shared + links., defaults to None + :type shared_link: Optional[AddShareLinkToFolderSharedLink], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '#add_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) + + def update_shared_link_on_folder( + self, + folder_id: str, + fields: str, + *, + shared_link: Optional[UpdateSharedLinkOnFolderSharedLink] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + """ + Updates a shared link on a folder. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: The settings for the shared link to update., defaults to None + :type shared_link: Optional[UpdateSharedLinkOnFolderSharedLink], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '#update_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) + + def remove_shared_link_from_folder( + self, + folder_id: str, + fields: str, + *, + shared_link: Union[ + Optional[RemoveSharedLinkFromFolderSharedLink], NullValue + ] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + """ + Removes a shared link from a folder. + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: By setting this value to `null`, the shared link + is removed from the folder., defaults to None + :type shared_link: Union[Optional[RemoveSharedLinkFromFolderSharedLink], NullValue], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '#remove_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) diff --git a/box_sdk_gen/managers/shared_links_web_links.py b/box_sdk_gen/managers/shared_links_web_links.py new file mode 100644 index 000000000..97ed09613 --- /dev/null +++ b/box_sdk_gen/managers/shared_links_web_links.py @@ -0,0 +1,508 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class AddShareLinkToWebLinkSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class AddShareLinkToWebLinkSharedLinkPermissionsField(BaseObject): + def __init__( + self, + *, + can_download: Optional[bool] = None, + can_preview: Optional[bool] = None, + can_edit: Optional[bool] = None, + **kwargs + ): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + :param can_preview: If the shared link allows for previewing of files. + This value is always `true`. For shared links on folders + this also applies to any items in the folder., defaults to None + :type can_preview: Optional[bool], optional + :param can_edit: This value can only be `true` is `type` is `file`., defaults to None + :type can_edit: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class AddShareLinkToWebLinkSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[AddShareLinkToWebLinkSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[AddShareLinkToWebLinkSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the file (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[AddShareLinkToWebLinkSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared + links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts. The value must be greater than the + current date and time., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class UpdateSharedLinkOnWebLinkSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class UpdateSharedLinkOnWebLinkSharedLinkPermissionsField(BaseObject): + def __init__( + self, + *, + can_download: Optional[bool] = None, + can_preview: Optional[bool] = None, + can_edit: Optional[bool] = None, + **kwargs + ): + """ + :param can_download: If the shared link allows for downloading of files. + This can only be set when `access` is set to + `open` or `company`., defaults to None + :type can_download: Optional[bool], optional + :param can_preview: If the shared link allows for previewing of files. + This value is always `true`. For shared links on folders + this also applies to any items in the folder., defaults to None + :type can_preview: Optional[bool], optional + :param can_edit: This value can only be `true` is `type` is `file`., defaults to None + :type can_edit: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class UpdateSharedLinkOnWebLinkSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateSharedLinkOnWebLinkSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[ + UpdateSharedLinkOnWebLinkSharedLinkPermissionsField + ] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the folder (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[UpdateSharedLinkOnWebLinkSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared + links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts. The value must be greater than the + current date and time., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + self.permissions = permissions + + +class RemoveSharedLinkFromWebLinkSharedLink(BaseObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class SharedLinksWebLinksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def find_web_link_for_shared_link( + self, + boxapi: str, + *, + fields: Optional[List[str]] = None, + if_none_match: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Returns the web link represented by a shared link. + + A shared web link can be represented by a shared link, + + + which can originate within the current enterprise or within another. + + + This endpoint allows an application to retrieve information about a + + + shared web link when only given a shared link. + + :param boxapi: A header containing the shared link and optional password for the + shared link. + + The format for this header is as follows: + + `shared_link=[link]&shared_link_password=[password]`. + :type boxapi: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param if_none_match: Ensures an item is only returned if it has changed. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `304 Not Modified` if the item has not + changed since., defaults to None + :type if_none_match: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + { + 'if-none-match': to_string(if_none_match), + 'boxapi': to_string(boxapi), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shared_items#web_links', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) + + def get_shared_link_for_web_link( + self, + web_link_id: str, + fields: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Gets the information for a shared link on a web link. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + '#get_shared_link', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) + + def add_share_link_to_web_link( + self, + web_link_id: str, + fields: str, + *, + shared_link: Optional[AddShareLinkToWebLinkSharedLink] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Adds a shared link to a web link. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: The settings for the shared link to create on the web link. + + Use an empty object (`{}`) to use the default settings for shared + links., defaults to None + :type shared_link: Optional[AddShareLinkToWebLinkSharedLink], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + '#add_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) + + def update_shared_link_on_web_link( + self, + web_link_id: str, + fields: str, + *, + shared_link: Optional[UpdateSharedLinkOnWebLinkSharedLink] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Updates a shared link on a web link. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: The settings for the shared link to update., defaults to None + :type shared_link: Optional[UpdateSharedLinkOnWebLinkSharedLink], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + '#update_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) + + def remove_shared_link_from_web_link( + self, + web_link_id: str, + fields: str, + *, + shared_link: Union[ + Optional[RemoveSharedLinkFromWebLinkSharedLink], NullValue + ] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Removes a shared link from a web link. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param fields: Explicitly request the `shared_link` fields + to be returned for this item. + :type fields: str + :param shared_link: By setting this value to `null`, the shared link + is removed from the web link., defaults to None + :type shared_link: Union[Optional[RemoveSharedLinkFromWebLinkSharedLink], NullValue], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shared_link': shared_link} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + '#remove_shared_link', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) diff --git a/box_sdk_gen/managers/shield_information_barrier_reports.py b/box_sdk_gen/managers/shield_information_barrier_reports.py new file mode 100644 index 000000000..d25c0bce8 --- /dev/null +++ b/box_sdk_gen/managers/shield_information_barrier_reports.py @@ -0,0 +1,183 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.schemas.shield_information_barrier_reports import ( + ShieldInformationBarrierReports, +) + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.shield_information_barrier_report import ( + ShieldInformationBarrierReport, +) + +from box_sdk_gen.schemas.shield_information_barrier_reference import ( + ShieldInformationBarrierReference, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class ShieldInformationBarrierReportsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_shield_information_barrier_reports( + self, + shield_information_barrier_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierReports: + """ + Lists shield information barrier reports. + :param shield_information_barrier_id: The ID of the shield information barrier. + :type shield_information_barrier_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'shield_information_barrier_id': to_string( + shield_information_barrier_id + ), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_reports', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierReports) + + def create_shield_information_barrier_report( + self, + *, + shield_information_barrier: Optional[ShieldInformationBarrierBase] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierReport: + """ + Creates a shield information barrier report for a given barrier. + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'shield_information_barrier': shield_information_barrier} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_reports', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierReport) + + def get_shield_information_barrier_report_by_id( + self, + shield_information_barrier_report_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierReport: + """ + Retrieves a shield information barrier report by its ID. + :param shield_information_barrier_report_id: The ID of the shield information barrier Report. + Example: "3423" + :type shield_information_barrier_report_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_reports/', + to_string(shield_information_barrier_report_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierReport) diff --git a/box_sdk_gen/managers/shield_information_barrier_segment_members.py b/box_sdk_gen/managers/shield_information_barrier_segment_members.py new file mode 100644 index 000000000..8580d3d54 --- /dev/null +++ b/box_sdk_gen/managers/shield_information_barrier_segment_members.py @@ -0,0 +1,282 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.shield_information_barrier_segment_member import ( + ShieldInformationBarrierSegmentMember, +) + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.shield_information_barrier_segment_members import ( + ShieldInformationBarrierSegmentMembers, +) + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateShieldInformationBarrierSegmentMemberType(str, Enum): + SHIELD_INFORMATION_BARRIER_SEGMENT_MEMBER = ( + 'shield_information_barrier_segment_member' + ) + + +class CreateShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentTypeField( + str, Enum +): + SHIELD_INFORMATION_BARRIER_SEGMENT = 'shield_information_barrier_segment' + + +class CreateShieldInformationBarrierSegmentMemberShieldInformationBarrierSegment( + BaseObject +): + _discriminator = 'type', {'shield_information_barrier_segment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ + CreateShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentTypeField + ] = None, + **kwargs + ): + """ + :param id: The ID reference of the + requesting shield information barrier segment., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield barrier segment for this member., defaults to None + :type type: Optional[CreateShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class ShieldInformationBarrierSegmentMembersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_shield_information_barrier_segment_member_by_id( + self, + shield_information_barrier_segment_member_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegmentMember: + """ + Retrieves a shield information barrier + + segment member by its ID. + + :param shield_information_barrier_segment_member_id: The ID of the shield information barrier segment Member. + Example: "7815" + :type shield_information_barrier_segment_member_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_members/', + to_string(shield_information_barrier_segment_member_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegmentMember) + + def delete_shield_information_barrier_segment_member_by_id( + self, + shield_information_barrier_segment_member_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a shield information barrier + + segment member based on provided ID. + + :param shield_information_barrier_segment_member_id: The ID of the shield information barrier segment Member. + Example: "7815" + :type shield_information_barrier_segment_member_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_members/', + to_string(shield_information_barrier_segment_member_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_shield_information_barrier_segment_members( + self, + shield_information_barrier_segment_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegmentMembers: + """ + Lists shield information barrier segment members + + based on provided segment IDs. + + :param shield_information_barrier_segment_id: The ID of the shield information barrier segment. + :type shield_information_barrier_segment_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'shield_information_barrier_segment_id': to_string( + shield_information_barrier_segment_id + ), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_members', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegmentMembers) + + def create_shield_information_barrier_segment_member( + self, + shield_information_barrier_segment: CreateShieldInformationBarrierSegmentMemberShieldInformationBarrierSegment, + user: UserBase, + *, + type: Optional[CreateShieldInformationBarrierSegmentMemberType] = None, + shield_information_barrier: Optional[ShieldInformationBarrierBase] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegmentMember: + """ + Creates a new shield information barrier segment member. + :param shield_information_barrier_segment: The `type` and `id` of the + requested shield information barrier segment. + :type shield_information_barrier_segment: CreateShieldInformationBarrierSegmentMemberShieldInformationBarrierSegment + :param user: User to which restriction will be applied. + :type user: UserBase + :param type: A type of the shield barrier segment member., defaults to None + :type type: Optional[CreateShieldInformationBarrierSegmentMemberType], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'type': type, + 'shield_information_barrier': shield_information_barrier, + 'shield_information_barrier_segment': shield_information_barrier_segment, + 'user': user, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_members', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegmentMember) diff --git a/box_sdk_gen/managers/shield_information_barrier_segment_restrictions.py b/box_sdk_gen/managers/shield_information_barrier_segment_restrictions.py new file mode 100644 index 000000000..e499efbe3 --- /dev/null +++ b/box_sdk_gen/managers/shield_information_barrier_segment_restrictions.py @@ -0,0 +1,316 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction import ( + ShieldInformationBarrierSegmentRestriction, +) + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.shield_information_barrier_segment_restrictions import ( + ShieldInformationBarrierSegmentRestrictions, +) + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateShieldInformationBarrierSegmentRestrictionType(str, Enum): + SHIELD_INFORMATION_BARRIER_SEGMENT_RESTRICTION = ( + 'shield_information_barrier_segment_restriction' + ) + + +class CreateShieldInformationBarrierSegmentRestrictionShieldInformationBarrierSegmentTypeField( + str, Enum +): + SHIELD_INFORMATION_BARRIER_SEGMENT = 'shield_information_barrier_segment' + + +class CreateShieldInformationBarrierSegmentRestrictionShieldInformationBarrierSegment( + BaseObject +): + _discriminator = 'type', {'shield_information_barrier_segment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ + CreateShieldInformationBarrierSegmentRestrictionShieldInformationBarrierSegmentTypeField + ] = None, + **kwargs + ): + """ + :param id: The ID reference of the requesting + shield information barrier segment., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield barrier segment for this member., defaults to None + :type type: Optional[CreateShieldInformationBarrierSegmentRestrictionShieldInformationBarrierSegmentTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateShieldInformationBarrierSegmentRestrictionRestrictedSegmentTypeField( + str, Enum +): + SHIELD_INFORMATION_BARRIER_SEGMENT = 'shield_information_barrier_segment' + + +class CreateShieldInformationBarrierSegmentRestrictionRestrictedSegment(BaseObject): + _discriminator = 'type', {'shield_information_barrier_segment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ + CreateShieldInformationBarrierSegmentRestrictionRestrictedSegmentTypeField + ] = None, + **kwargs + ): + """ + :param id: The ID reference of the restricted + shield information barrier segment., defaults to None + :type id: Optional[str], optional + :param type: The type of the restricted shield + information barrier segment., defaults to None + :type type: Optional[CreateShieldInformationBarrierSegmentRestrictionRestrictedSegmentTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class ShieldInformationBarrierSegmentRestrictionsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_shield_information_barrier_segment_restriction_by_id( + self, + shield_information_barrier_segment_restriction_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegmentRestriction: + """ + Retrieves a shield information barrier segment + + restriction based on provided ID. + + :param shield_information_barrier_segment_restriction_id: The ID of the shield information barrier segment Restriction. + Example: "4563" + :type shield_information_barrier_segment_restriction_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_restrictions/', + to_string(shield_information_barrier_segment_restriction_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegmentRestriction) + + def delete_shield_information_barrier_segment_restriction_by_id( + self, + shield_information_barrier_segment_restriction_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Delete shield information barrier segment restriction + + based on provided ID. + + :param shield_information_barrier_segment_restriction_id: The ID of the shield information barrier segment Restriction. + Example: "4563" + :type shield_information_barrier_segment_restriction_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_restrictions/', + to_string(shield_information_barrier_segment_restriction_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_shield_information_barrier_segment_restrictions( + self, + shield_information_barrier_segment_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegmentRestrictions: + """ + Lists shield information barrier segment restrictions + + based on provided segment ID. + + :param shield_information_barrier_segment_id: The ID of the shield information barrier segment. + :type shield_information_barrier_segment_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'shield_information_barrier_segment_id': to_string( + shield_information_barrier_segment_id + ), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_restrictions', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegmentRestrictions) + + def create_shield_information_barrier_segment_restriction( + self, + shield_information_barrier_segment: CreateShieldInformationBarrierSegmentRestrictionShieldInformationBarrierSegment, + restricted_segment: CreateShieldInformationBarrierSegmentRestrictionRestrictedSegment, + *, + type: CreateShieldInformationBarrierSegmentRestrictionType = CreateShieldInformationBarrierSegmentRestrictionType.SHIELD_INFORMATION_BARRIER_SEGMENT_RESTRICTION, + shield_information_barrier: Optional[ShieldInformationBarrierBase] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegmentRestriction: + """ + Creates a shield information barrier + + segment restriction object. + + :param shield_information_barrier_segment: The `type` and `id` of the requested + shield information barrier segment. + :type shield_information_barrier_segment: CreateShieldInformationBarrierSegmentRestrictionShieldInformationBarrierSegment + :param restricted_segment: The `type` and `id` of the restricted + shield information barrier segment. + :type restricted_segment: CreateShieldInformationBarrierSegmentRestrictionRestrictedSegment + :param type: The type of the shield barrier segment + restriction for this member., defaults to CreateShieldInformationBarrierSegmentRestrictionType.SHIELD_INFORMATION_BARRIER_SEGMENT_RESTRICTION + :type type: CreateShieldInformationBarrierSegmentRestrictionType, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'type': type, + 'shield_information_barrier': shield_information_barrier, + 'shield_information_barrier_segment': shield_information_barrier_segment, + 'restricted_segment': restricted_segment, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segment_restrictions', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegmentRestriction) diff --git a/box_sdk_gen/managers/shield_information_barrier_segments.py b/box_sdk_gen/managers/shield_information_barrier_segments.py new file mode 100644 index 000000000..d258edeca --- /dev/null +++ b/box_sdk_gen/managers/shield_information_barrier_segments.py @@ -0,0 +1,279 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.shield_information_barrier_segment import ( + ShieldInformationBarrierSegment, +) + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.shield_information_barrier_segments import ( + ShieldInformationBarrierSegments, +) + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class ShieldInformationBarrierSegmentsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_shield_information_barrier_segment_by_id( + self, + shield_information_barrier_segment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegment: + """ + Retrieves shield information barrier segment based on provided ID.. + :param shield_information_barrier_segment_id: The ID of the shield information barrier segment. + Example: "3423" + :type shield_information_barrier_segment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segments/', + to_string(shield_information_barrier_segment_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegment) + + def delete_shield_information_barrier_segment_by_id( + self, + shield_information_barrier_segment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes the shield information barrier segment + + based on provided ID. + + :param shield_information_barrier_segment_id: The ID of the shield information barrier segment. + Example: "3423" + :type shield_information_barrier_segment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segments/', + to_string(shield_information_barrier_segment_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def update_shield_information_barrier_segment_by_id( + self, + shield_information_barrier_segment_id: str, + *, + name: Optional[str] = None, + description: Union[Optional[str], NullValue] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegment: + """ + Updates the shield information barrier segment based on provided ID.. + :param shield_information_barrier_segment_id: The ID of the shield information barrier segment. + Example: "3423" + :type shield_information_barrier_segment_id: str + :param name: The updated name for the shield information barrier segment., defaults to None + :type name: Optional[str], optional + :param description: The updated description for + the shield information barrier segment., defaults to None + :type description: Union[Optional[str], NullValue], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name, 'description': description} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segments/', + to_string(shield_information_barrier_segment_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegment) + + def get_shield_information_barrier_segments( + self, + shield_information_barrier_id: str, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegments: + """ + Retrieves a list of shield information barrier segment objects + + for the specified Information Barrier ID. + + :param shield_information_barrier_id: The ID of the shield information barrier. + :type shield_information_barrier_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'shield_information_barrier_id': to_string( + shield_information_barrier_id + ), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segments', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegments) + + def create_shield_information_barrier_segment( + self, + shield_information_barrier: ShieldInformationBarrierBase, + name: str, + *, + description: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrierSegment: + """ + Creates a shield information barrier segment. + :param name: Name of the shield information barrier segment. + :type name: str + :param description: Description of the shield information barrier segment., defaults to None + :type description: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'shield_information_barrier': shield_information_barrier, + 'name': name, + 'description': description, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barrier_segments', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrierSegment) diff --git a/box_sdk_gen/managers/shield_information_barriers.py b/box_sdk_gen/managers/shield_information_barriers.py new file mode 100644 index 000000000..b3784dae2 --- /dev/null +++ b/box_sdk_gen/managers/shield_information_barriers.py @@ -0,0 +1,219 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.shield_information_barrier import ShieldInformationBarrier + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.shield_information_barriers import ShieldInformationBarriers + +from box_sdk_gen.schemas.enterprise_base import EnterpriseBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class UpdateShieldInformationBarrierStatusStatus(str, Enum): + PENDING = 'pending' + DISABLED = 'disabled' + + +class ShieldInformationBarriersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_shield_information_barrier_by_id( + self, + shield_information_barrier_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrier: + """ + Get shield information barrier based on provided ID. + :param shield_information_barrier_id: The ID of the shield information barrier. + Example: "1910967" + :type shield_information_barrier_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barriers/', + to_string(shield_information_barrier_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrier) + + def update_shield_information_barrier_status( + self, + id: str, + status: UpdateShieldInformationBarrierStatusStatus, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrier: + """ + Change status of shield information barrier with the specified ID. + :param id: The ID of the shield information barrier. + :type id: str + :param status: The desired status for the shield information barrier. + :type status: UpdateShieldInformationBarrierStatusStatus + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'id': id, 'status': status} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barriers/change_status', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrier) + + def get_shield_information_barriers( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarriers: + """ + Retrieves a list of shield information barrier objects + + for the enterprise of JWT. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barriers', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarriers) + + def create_shield_information_barrier( + self, + enterprise: EnterpriseBase, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldInformationBarrier: + """ + Creates a shield information barrier to + + separate individuals/groups within the same + + + firm and prevents confidential information passing between them. + + :param enterprise: The `type` and `id` of enterprise this barrier is under. + :type enterprise: EnterpriseBase + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'enterprise': enterprise} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_information_barriers', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldInformationBarrier) diff --git a/box_sdk_gen/managers/shield_lists.py b/box_sdk_gen/managers/shield_lists.py new file mode 100644 index 000000000..ec363b9a3 --- /dev/null +++ b/box_sdk_gen/managers/shield_lists.py @@ -0,0 +1,300 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_country_v2025_r0 import ( + ShieldListContentCountryV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_domain_v2025_r0 import ( + ShieldListContentDomainV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_email_v2025_r0 import ( + ShieldListContentEmailV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_ip_v2025_r0 import ( + ShieldListContentIpV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_request_v2025_r0 import ( + ShieldListContentRequestV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_lists_v2025_r0 import ShieldListsV2025R0 + +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import ( + BoxVersionHeaderV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_v2025_r0 import ShieldListV2025R0 + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import ClientErrorV2025R0 + +from box_sdk_gen.schemas.v2025_r0.shield_lists_create_v2025_r0 import ( + ShieldListsCreateV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_lists_update_v2025_r0 import ( + ShieldListsUpdateV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class ShieldListsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_shield_lists_v2025_r0( + self, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldListsV2025R0: + """ + Retrieves all shield lists in the enterprise. + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/shield_lists'] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldListsV2025R0) + + def create_shield_list_v2025_r0( + self, + name: str, + content: ShieldListContentRequestV2025R0, + *, + description: Optional[str] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldListV2025R0: + """ + Creates a shield list. + :param name: The name of the shield list. + :type name: str + :param description: Optional description of Shield List., defaults to None + :type description: Optional[str], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'description': description, + 'content': content, + } + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/shield_lists'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldListV2025R0) + + def get_shield_list_by_id_v2025_r0( + self, + shield_list_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldListV2025R0: + """ + Retrieves a single shield list by its ID. + :param shield_list_id: The unique identifier that represents a shield list. + The ID for any Shield List can be determined by the response from the endpoint + fetching all shield lists for the enterprise. + Example: "90fb0e17-c332-40ed-b4f9-fa8908fbbb24 " + :type shield_list_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_lists/', + to_string(shield_list_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldListV2025R0) + + def delete_shield_list_by_id_v2025_r0( + self, + shield_list_id: str, + *, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Delete a single shield list by its ID. + :param shield_list_id: The unique identifier that represents a shield list. + The ID for any Shield List can be determined by the response from the endpoint + fetching all shield lists for the enterprise. + Example: "90fb0e17-c332-40ed-b4f9-fa8908fbbb24 " + :type shield_list_id: str + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_lists/', + to_string(shield_list_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def update_shield_list_by_id_v2025_r0( + self, + shield_list_id: str, + name: str, + content: ShieldListContentRequestV2025R0, + *, + description: Optional[str] = None, + box_version: BoxVersionHeaderV2025R0 = BoxVersionHeaderV2025R0._2025_0, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ShieldListV2025R0: + """ + Updates a shield list. + :param shield_list_id: The unique identifier that represents a shield list. + The ID for any Shield List can be determined by the response from the endpoint + fetching all shield lists for the enterprise. + Example: "90fb0e17-c332-40ed-b4f9-fa8908fbbb24 " + :type shield_list_id: str + :param name: The name of the shield list. + :type name: str + :param description: Optional description of Shield List., defaults to None + :type description: Optional[str], optional + :param box_version: Version header., defaults to BoxVersionHeaderV2025R0._2025_0 + :type box_version: BoxVersionHeaderV2025R0, optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'description': description, + 'content': content, + } + headers_map: Dict[str, str] = prepare_params( + {'box-version': to_string(box_version), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/shield_lists/', + to_string(shield_list_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ShieldListV2025R0) diff --git a/box_sdk_gen/managers/sign_requests.py b/box_sdk_gen/managers/sign_requests.py new file mode 100644 index 000000000..bef15282c --- /dev/null +++ b/box_sdk_gen/managers/sign_requests.py @@ -0,0 +1,348 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from typing import List + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.sign_request_create_signer import SignRequestCreateSigner + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.sign_request_prefill_tag import SignRequestPrefillTag + +from box_sdk_gen.schemas.sign_request import SignRequest + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.sign_requests import SignRequests + +from box_sdk_gen.schemas.sign_request_create_request import SignRequestCreateRequest + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateSignRequestSignatureColor(str, Enum): + BLUE = 'blue' + BLACK = 'black' + RED = 'red' + + +class SignRequestsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def cancel_sign_request( + self, + sign_request_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SignRequest: + """ + Cancels a sign request. + :param sign_request_id: The ID of the signature request. + Example: "33243242" + :type sign_request_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/sign_requests/', + to_string(sign_request_id), + '/cancel', + ] + ), + method='POST', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SignRequest) + + def resend_sign_request( + self, + sign_request_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Resends a signature request email to all outstanding signers. + :param sign_request_id: The ID of the signature request. + Example: "33243242" + :type sign_request_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/sign_requests/', + to_string(sign_request_id), + '/resend', + ] + ), + method='POST', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def get_sign_request_by_id( + self, + sign_request_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SignRequest: + """ + Gets a sign request by ID. + :param sign_request_id: The ID of the signature request. + Example: "33243242" + :type sign_request_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/sign_requests/', + to_string(sign_request_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SignRequest) + + def get_sign_requests( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + senders: Optional[List[str]] = None, + shared_requests: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SignRequests: + """ + Gets signature requests created by a user. If the `sign_files` and/or + + `parent_folder` are deleted, the signature request will not return in the list. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param senders: A list of sender emails to filter the signature requests by sender. + If provided, `shared_requests` must be set to `true`., defaults to None + :type senders: Optional[List[str]], optional + :param shared_requests: If set to `true`, only includes requests that user is not an owner, + but user is a collaborator. Collaborator access is determined by the + user access level of the sign files of the request. + Default is `false`. Must be set to `true` if `senders` are provided., defaults to None + :type shared_requests: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'marker': to_string(marker), + 'limit': to_string(limit), + 'senders': to_string(senders), + 'shared_requests': to_string(shared_requests), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/sign_requests'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SignRequests) + + def create_sign_request( + self, + signers: List[SignRequestCreateSigner], + *, + source_files: Union[Optional[List[FileBase]], NullValue] = None, + signature_color: Union[ + Optional[CreateSignRequestSignatureColor], NullValue + ] = None, + parent_folder: Optional[FolderMini] = None, + is_document_preparation_needed: Optional[bool] = None, + redirect_url: Union[Optional[str], NullValue] = None, + declined_redirect_url: Union[Optional[str], NullValue] = None, + are_text_signatures_enabled: Optional[bool] = None, + email_subject: Union[Optional[str], NullValue] = None, + email_message: Union[Optional[str], NullValue] = None, + are_reminders_enabled: Optional[bool] = None, + name: Optional[str] = None, + prefill_tags: Optional[List[SignRequestPrefillTag]] = None, + days_valid: Union[Optional[int], NullValue] = None, + external_id: Union[Optional[str], NullValue] = None, + template_id: Union[Optional[str], NullValue] = None, + external_system_name: Union[Optional[str], NullValue] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SignRequest: + """ + Creates a signature request. This involves preparing a document for signing and + + sending the signature request to signers. + + :param signers: Array of signers for the signature request. 35 is the + max number of signers permitted. + + **Note**: It may happen that some signers belong to conflicting [segments](r://shield-information-barrier-segment-member) (user groups). + This means that due to the security policies, users are assigned to segments to prevent exchanges or communication that could lead to ethical conflicts. + In such a case, an attempt to send the sign request will result in an error. + + Read more about [segments and ethical walls](https://support.box.com/hc/en-us/articles/9920431507603-Understanding-Information-Barriers#h_01GFVJEHQA06N7XEZ4GCZ9GFAQ). + :type signers: List[SignRequestCreateSigner] + :param source_files: List of files to create a signing document from. This is currently limited to ten files. Only the ID and type fields are required for each file., defaults to None + :type source_files: Union[Optional[List[FileBase]], NullValue], optional + :param signature_color: Force a specific color for the signature (blue, black, or red)., defaults to None + :type signature_color: Union[Optional[CreateSignRequestSignatureColor], NullValue], optional + :param is_document_preparation_needed: Indicates if the sender should receive a `prepare_url` in the response to complete document preparation using the UI., defaults to None + :type is_document_preparation_needed: Optional[bool], optional + :param redirect_url: When specified, the signature request will be redirected to this url when a document is signed., defaults to None + :type redirect_url: Union[Optional[str], NullValue], optional + :param declined_redirect_url: The uri that a signer will be redirected to after declining to sign a document., defaults to None + :type declined_redirect_url: Union[Optional[str], NullValue], optional + :param are_text_signatures_enabled: Disables the usage of signatures generated by typing (text)., defaults to None + :type are_text_signatures_enabled: Optional[bool], optional + :param email_subject: Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used., defaults to None + :type email_subject: Union[Optional[str], NullValue], optional + :param email_message: Message to include in sign request email. The field is cleaned through sanitization of specific characters. However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email. The message may contain the following html tags including `a`, `abbr`, `acronym`, `b`, `blockquote`, `code`, `em`, `i`, `ul`, `li`, `ol`, and `strong`. Be aware that when the text to html ratio is too high, the email may end up in spam filters. Custom styles on these tags are not allowed. If this field is not passed, a default message will be used., defaults to None + :type email_message: Union[Optional[str], NullValue], optional + :param are_reminders_enabled: Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers., defaults to None + :type are_reminders_enabled: Optional[bool], optional + :param name: Name of the signature request., defaults to None + :type name: Optional[str], optional + :param prefill_tags: When a document contains sign-related tags in the content, you can prefill them using this `prefill_tags` by referencing the 'id' of the tag as the `external_id` field of the prefill tag., defaults to None + :type prefill_tags: Optional[List[SignRequestPrefillTag]], optional + :param days_valid: Set the number of days after which the created signature request will automatically expire if not completed. By default, we do not apply any expiration date on signature requests, and the signature request does not expire., defaults to None + :type days_valid: Union[Optional[int], NullValue], optional + :param external_id: This can be used to reference an ID in an external system that the sign request is related to., defaults to None + :type external_id: Union[Optional[str], NullValue], optional + :param template_id: When a signature request is created from a template this field will indicate the id of that template., defaults to None + :type template_id: Union[Optional[str], NullValue], optional + :param external_system_name: Used as an optional system name to appear in the signature log next to the signers who have been assigned the `embed_url_external_id`., defaults to None + :type external_system_name: Union[Optional[str], NullValue], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'source_files': source_files, + 'signature_color': signature_color, + 'signers': signers, + 'parent_folder': parent_folder, + 'is_document_preparation_needed': is_document_preparation_needed, + 'redirect_url': redirect_url, + 'declined_redirect_url': declined_redirect_url, + 'are_text_signatures_enabled': are_text_signatures_enabled, + 'email_subject': email_subject, + 'email_message': email_message, + 'are_reminders_enabled': are_reminders_enabled, + 'name': name, + 'prefill_tags': prefill_tags, + 'days_valid': days_valid, + 'external_id': external_id, + 'template_id': template_id, + 'external_system_name': external_system_name, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/sign_requests'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SignRequest) diff --git a/box_sdk_gen/managers/sign_templates.py b/box_sdk_gen/managers/sign_templates.py new file mode 100644 index 000000000..531c1f0c6 --- /dev/null +++ b/box_sdk_gen/managers/sign_templates.py @@ -0,0 +1,123 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.sign_templates import SignTemplates + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.sign_template import SignTemplate + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class SignTemplatesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_sign_templates( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SignTemplates: + """ + Gets Box Sign templates created by a user. + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/sign_templates'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SignTemplates) + + def get_sign_template_by_id( + self, + template_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SignTemplate: + """ + Fetches details of a specific Box Sign template. + :param template_id: The ID of a Box Sign template. + Example: "123075213-7d117509-8f05-42e4-a5ef-5190a319d41d" + :type template_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/sign_templates/', + to_string(template_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SignTemplate) diff --git a/box_sdk_gen/managers/skills.py b/box_sdk_gen/managers/skills.py new file mode 100644 index 000000000..697d72656 --- /dev/null +++ b/box_sdk_gen/managers/skills.py @@ -0,0 +1,414 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.schemas.keyword_skill_card import KeywordSkillCard + +from box_sdk_gen.schemas.timeline_skill_card import TimelineSkillCard + +from box_sdk_gen.schemas.transcript_skill_card import TranscriptSkillCard + +from box_sdk_gen.schemas.status_skill_card import StatusSkillCard + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.skill_cards_metadata import SkillCardsMetadata + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.skill_card import SkillCard + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class UpdateBoxSkillCardsOnFileRequestBodyOpField(str, Enum): + REPLACE = 'replace' + + +class UpdateBoxSkillCardsOnFileRequestBody(BaseObject): + def __init__( + self, + *, + op: Optional[UpdateBoxSkillCardsOnFileRequestBodyOpField] = None, + path: Optional[str] = None, + value: Optional[SkillCard] = None, + **kwargs + ): + """ + :param op: The value will always be `replace`., defaults to None + :type op: Optional[UpdateBoxSkillCardsOnFileRequestBodyOpField], optional + :param path: The JSON Path that represents the card to replace. In most cases + this will be in the format `/cards/{index}` where `index` is the + zero-indexed position of the card in the list of cards., defaults to None + :type path: Optional[str], optional + """ + super().__init__(**kwargs) + self.op = op + self.path = path + self.value = value + + +class UpdateAllSkillCardsOnFileStatus(str, Enum): + INVOKED = 'invoked' + PROCESSING = 'processing' + SUCCESS = 'success' + TRANSIENT_FAILURE = 'transient_failure' + PERMANENT_FAILURE = 'permanent_failure' + + +class UpdateAllSkillCardsOnFileMetadata(BaseObject): + def __init__(self, *, cards: Optional[List[SkillCard]] = None, **kwargs): + """ + :param cards: A list of Box Skill cards to apply to this file., defaults to None + :type cards: Optional[List[SkillCard]], optional + """ + super().__init__(**kwargs) + self.cards = cards + + +class UpdateAllSkillCardsOnFileFileTypeField(str, Enum): + FILE = 'file' + + +class UpdateAllSkillCardsOnFileFile(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + *, + type: Optional[UpdateAllSkillCardsOnFileFileTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `file`., defaults to None + :type type: Optional[UpdateAllSkillCardsOnFileFileTypeField], optional + :param id: The ID of the file., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class UpdateAllSkillCardsOnFileFileVersionTypeField(str, Enum): + FILE_VERSION = 'file_version' + + +class UpdateAllSkillCardsOnFileFileVersion(BaseObject): + _discriminator = 'type', {'file_version'} + + def __init__( + self, + *, + type: Optional[UpdateAllSkillCardsOnFileFileVersionTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `file_version`., defaults to None + :type type: Optional[UpdateAllSkillCardsOnFileFileVersionTypeField], optional + :param id: The ID of the file version., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class UpdateAllSkillCardsOnFileUsage(BaseObject): + def __init__( + self, *, unit: Optional[str] = None, value: Optional[float] = None, **kwargs + ): + """ + :param unit: The value will always be `file`., defaults to None + :type unit: Optional[str], optional + :param value: Number of resources affected., defaults to None + :type value: Optional[float], optional + """ + super().__init__(**kwargs) + self.unit = unit + self.value = value + + +class SkillsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_box_skill_cards_on_file( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SkillCardsMetadata: + """ + List the Box Skills metadata cards that are attached to a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/global/boxSkillsCards', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SkillCardsMetadata) + + def create_box_skill_cards_on_file( + self, + file_id: str, + cards: List[SkillCard], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SkillCardsMetadata: + """ + Applies one or more Box Skills metadata cards to a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param cards: A list of Box Skill cards to apply to this file. + :type cards: List[SkillCard] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'cards': cards} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/global/boxSkillsCards', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SkillCardsMetadata) + + def update_box_skill_cards_on_file( + self, + file_id: str, + request_body: List[UpdateBoxSkillCardsOnFileRequestBody], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> SkillCardsMetadata: + """ + Updates one or more Box Skills metadata cards to a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param request_body: Request body of updateBoxSkillCardsOnFile method + :type request_body: List[UpdateBoxSkillCardsOnFileRequestBody] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/global/boxSkillsCards', + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json-patch+json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, SkillCardsMetadata) + + def delete_box_skill_cards_from_file( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes any Box Skills cards metadata from a file. + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/metadata/global/boxSkillsCards', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def update_all_skill_cards_on_file( + self, + skill_id: str, + status: UpdateAllSkillCardsOnFileStatus, + metadata: UpdateAllSkillCardsOnFileMetadata, + file: UpdateAllSkillCardsOnFileFile, + *, + file_version: Optional[UpdateAllSkillCardsOnFileFileVersion] = None, + usage: Optional[UpdateAllSkillCardsOnFileUsage] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + An alternative method that can be used to overwrite and update all Box Skill + + metadata cards on a file. + + :param skill_id: The ID of the skill to apply this metadata for. + Example: "33243242" + :type skill_id: str + :param status: Defines the status of this invocation. Set this to `success` when setting Skill cards. + :type status: UpdateAllSkillCardsOnFileStatus + :param metadata: The metadata to set for this skill. This is a list of + Box Skills cards. These cards will overwrite any existing Box + skill cards on the file. + :type metadata: UpdateAllSkillCardsOnFileMetadata + :param file: The file to assign the cards to. + :type file: UpdateAllSkillCardsOnFileFile + :param file_version: The optional file version to assign the cards to., defaults to None + :type file_version: Optional[UpdateAllSkillCardsOnFileFileVersion], optional + :param usage: A descriptor that defines what items are affected by this call. + + Set this to the default values when setting a card to a `success` + state, and leave it out in most other situations., defaults to None + :type usage: Optional[UpdateAllSkillCardsOnFileUsage], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'status': status, + 'metadata': metadata, + 'file': file, + 'file_version': file_version, + 'usage': usage, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/skill_invocations/', + to_string(skill_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/storage_policies.py b/box_sdk_gen/managers/storage_policies.py new file mode 100644 index 000000000..54f08cb97 --- /dev/null +++ b/box_sdk_gen/managers/storage_policies.py @@ -0,0 +1,140 @@ +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.storage_policies import StoragePolicies + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.storage_policy import StoragePolicy + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class StoragePoliciesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_storage_policies( + self, + *, + fields: Optional[List[str]] = None, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> StoragePolicies: + """ + Fetches all the storage policies in the enterprise. + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'marker': to_string(marker), + 'limit': to_string(limit), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/storage_policies'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, StoragePolicies) + + def get_storage_policy_by_id( + self, + storage_policy_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> StoragePolicy: + """ + Fetches a specific storage policy. + :param storage_policy_id: The ID of the storage policy. + Example: "34342" + :type storage_policy_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/storage_policies/', + to_string(storage_policy_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, StoragePolicy) diff --git a/box_sdk_gen/managers/storage_policy_assignments.py b/box_sdk_gen/managers/storage_policy_assignments.py new file mode 100644 index 000000000..be0f88b2a --- /dev/null +++ b/box_sdk_gen/managers/storage_policy_assignments.py @@ -0,0 +1,352 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.storage_policy_assignments import StoragePolicyAssignments + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.storage_policy_assignment import StoragePolicyAssignment + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetStoragePolicyAssignmentsResolvedForType(str, Enum): + USER = 'user' + ENTERPRISE = 'enterprise' + + +class CreateStoragePolicyAssignmentStoragePolicyTypeField(str, Enum): + STORAGE_POLICY = 'storage_policy' + + +class CreateStoragePolicyAssignmentStoragePolicy(BaseObject): + _discriminator = 'type', {'storage_policy'} + + def __init__( + self, + id: str, + *, + type: CreateStoragePolicyAssignmentStoragePolicyTypeField = CreateStoragePolicyAssignmentStoragePolicyTypeField.STORAGE_POLICY, + **kwargs + ): + """ + :param id: The ID of the storage policy to assign. + :type id: str + :param type: The type to assign., defaults to CreateStoragePolicyAssignmentStoragePolicyTypeField.STORAGE_POLICY + :type type: CreateStoragePolicyAssignmentStoragePolicyTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateStoragePolicyAssignmentAssignedToTypeField(str, Enum): + USER = 'user' + ENTERPRISE = 'enterprise' + + +class CreateStoragePolicyAssignmentAssignedTo(BaseObject): + _discriminator = 'type', {'user', 'enterprise'} + + def __init__( + self, type: CreateStoragePolicyAssignmentAssignedToTypeField, id: str, **kwargs + ): + """ + :param type: The type to assign the policy to. + :type type: CreateStoragePolicyAssignmentAssignedToTypeField + :param id: The ID of the user or enterprise. + :type id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class UpdateStoragePolicyAssignmentByIdStoragePolicyTypeField(str, Enum): + STORAGE_POLICY = 'storage_policy' + + +class UpdateStoragePolicyAssignmentByIdStoragePolicy(BaseObject): + _discriminator = 'type', {'storage_policy'} + + def __init__( + self, + id: str, + *, + type: UpdateStoragePolicyAssignmentByIdStoragePolicyTypeField = UpdateStoragePolicyAssignmentByIdStoragePolicyTypeField.STORAGE_POLICY, + **kwargs + ): + """ + :param id: The ID of the storage policy to assign. + :type id: str + :param type: The type to assign., defaults to UpdateStoragePolicyAssignmentByIdStoragePolicyTypeField.STORAGE_POLICY + :type type: UpdateStoragePolicyAssignmentByIdStoragePolicyTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class StoragePolicyAssignmentsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_storage_policy_assignments( + self, + resolved_for_type: GetStoragePolicyAssignmentsResolvedForType, + resolved_for_id: str, + *, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> StoragePolicyAssignments: + """ + Fetches all the storage policy assignment for an enterprise or user. + :param resolved_for_type: The target type to return assignments for. + :type resolved_for_type: GetStoragePolicyAssignmentsResolvedForType + :param resolved_for_id: The ID of the user or enterprise to return assignments for. + :type resolved_for_id: str + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'marker': to_string(marker), + 'resolved_for_type': to_string(resolved_for_type), + 'resolved_for_id': to_string(resolved_for_id), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/storage_policy_assignments', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, StoragePolicyAssignments) + + def create_storage_policy_assignment( + self, + storage_policy: CreateStoragePolicyAssignmentStoragePolicy, + assigned_to: CreateStoragePolicyAssignmentAssignedTo, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> StoragePolicyAssignment: + """ + Creates a storage policy assignment for an enterprise or user. + :param storage_policy: The storage policy to assign to the user or + enterprise. + :type storage_policy: CreateStoragePolicyAssignmentStoragePolicy + :param assigned_to: The user or enterprise to assign the storage + policy to. + :type assigned_to: CreateStoragePolicyAssignmentAssignedTo + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'storage_policy': storage_policy, + 'assigned_to': assigned_to, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/storage_policy_assignments', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, StoragePolicyAssignment) + + def get_storage_policy_assignment_by_id( + self, + storage_policy_assignment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> StoragePolicyAssignment: + """ + Fetches a specific storage policy assignment. + :param storage_policy_assignment_id: The ID of the storage policy assignment. + Example: "932483" + :type storage_policy_assignment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/storage_policy_assignments/', + to_string(storage_policy_assignment_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, StoragePolicyAssignment) + + def update_storage_policy_assignment_by_id( + self, + storage_policy_assignment_id: str, + storage_policy: UpdateStoragePolicyAssignmentByIdStoragePolicy, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> StoragePolicyAssignment: + """ + Updates a specific storage policy assignment. + :param storage_policy_assignment_id: The ID of the storage policy assignment. + Example: "932483" + :type storage_policy_assignment_id: str + :param storage_policy: The storage policy to assign to the user or + enterprise. + :type storage_policy: UpdateStoragePolicyAssignmentByIdStoragePolicy + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'storage_policy': storage_policy} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/storage_policy_assignments/', + to_string(storage_policy_assignment_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, StoragePolicyAssignment) + + def delete_storage_policy_assignment_by_id( + self, + storage_policy_assignment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Delete a storage policy assignment. + + Deleting a storage policy assignment on a user + + + will have the user inherit the enterprise's default + + + storage policy. + + + There is a rate limit for calling this endpoint of only + + + twice per user in a 24 hour time frame. + + :param storage_policy_assignment_id: The ID of the storage policy assignment. + Example: "932483" + :type storage_policy_assignment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/storage_policy_assignments/', + to_string(storage_policy_assignment_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/task_assignments.py b/box_sdk_gen/managers/task_assignments.py new file mode 100644 index 000000000..f7d5a8139 --- /dev/null +++ b/box_sdk_gen/managers/task_assignments.py @@ -0,0 +1,303 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.task_assignments import TaskAssignments + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.task_assignment import TaskAssignment + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateTaskAssignmentTaskTypeField(str, Enum): + TASK = 'task' + + +class CreateTaskAssignmentTask(BaseObject): + _discriminator = 'type', {'task'} + + def __init__( + self, + id: str, + *, + type: CreateTaskAssignmentTaskTypeField = CreateTaskAssignmentTaskTypeField.TASK, + **kwargs + ): + """ + :param id: The ID of the task. + :type id: str + :param type: The type of the item to assign., defaults to CreateTaskAssignmentTaskTypeField.TASK + :type type: CreateTaskAssignmentTaskTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateTaskAssignmentAssignTo(BaseObject): + def __init__( + self, *, id: Optional[str] = None, login: Optional[str] = None, **kwargs + ): + """ + :param id: The ID of the user to assign to the + task. + + To specify a user by their email + address use the `login` parameter., defaults to None + :type id: Optional[str], optional + :param login: The email address of the user to assign to the task. + To specify a user by their user ID please use the `id` parameter., defaults to None + :type login: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.login = login + + +class UpdateTaskAssignmentByIdResolutionState(str, Enum): + COMPLETED = 'completed' + INCOMPLETE = 'incomplete' + APPROVED = 'approved' + REJECTED = 'rejected' + + +class TaskAssignmentsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_task_assignments( + self, task_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TaskAssignments: + """ + Lists all of the assignments for a given task. + :param task_id: The ID of the task. + Example: "12345" + :type task_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/tasks/', + to_string(task_id), + '/assignments', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TaskAssignments) + + def create_task_assignment( + self, + task: CreateTaskAssignmentTask, + assign_to: CreateTaskAssignmentAssignTo, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TaskAssignment: + """ + Assigns a task to a user. + + A task can be assigned to more than one user by creating multiple + + + assignments. + + :param task: The task to assign to a user. + :type task: CreateTaskAssignmentTask + :param assign_to: The user to assign the task to. + :type assign_to: CreateTaskAssignmentAssignTo + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'task': task, 'assign_to': assign_to} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/task_assignments'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TaskAssignment) + + def get_task_assignment_by_id( + self, + task_assignment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TaskAssignment: + """ + Retrieves information about a task assignment. + :param task_assignment_id: The ID of the task assignment. + Example: "12345" + :type task_assignment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/task_assignments/', + to_string(task_assignment_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TaskAssignment) + + def update_task_assignment_by_id( + self, + task_assignment_id: str, + *, + message: Optional[str] = None, + resolution_state: Optional[UpdateTaskAssignmentByIdResolutionState] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TaskAssignment: + """ + Updates a task assignment. This endpoint can be + + used to update the state of a task assigned to a user. + + :param task_assignment_id: The ID of the task assignment. + Example: "12345" + :type task_assignment_id: str + :param message: An optional message by the assignee that can be added to the task., defaults to None + :type message: Optional[str], optional + :param resolution_state: The state of the task assigned to the user. + + * For a task with an `action` value of `complete` this can be + `incomplete` or `completed`. + * For a task with an `action` of `review` this can be + `incomplete`, `approved`, or `rejected`., defaults to None + :type resolution_state: Optional[UpdateTaskAssignmentByIdResolutionState], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'message': message, 'resolution_state': resolution_state} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/task_assignments/', + to_string(task_assignment_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TaskAssignment) + + def delete_task_assignment_by_id( + self, + task_assignment_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a specific task assignment. + :param task_assignment_id: The ID of the task assignment. + Example: "12345" + :type task_assignment_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/task_assignments/', + to_string(task_assignment_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/tasks.py b/box_sdk_gen/managers/tasks.py new file mode 100644 index 000000000..d6b13c19c --- /dev/null +++ b/box_sdk_gen/managers/tasks.py @@ -0,0 +1,338 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.tasks import Tasks + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.task import Task + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class CreateTaskItemTypeField(str, Enum): + FILE = 'file' + + +class CreateTaskItem(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CreateTaskItemTypeField] = None, + **kwargs + ): + """ + :param id: The ID of the file., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `file`., defaults to None + :type type: Optional[CreateTaskItemTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateTaskAction(str, Enum): + REVIEW = 'review' + COMPLETE = 'complete' + + +class CreateTaskCompletionRule(str, Enum): + ALL_ASSIGNEES = 'all_assignees' + ANY_ASSIGNEE = 'any_assignee' + + +class UpdateTaskByIdAction(str, Enum): + REVIEW = 'review' + COMPLETE = 'complete' + + +class UpdateTaskByIdCompletionRule(str, Enum): + ALL_ASSIGNEES = 'all_assignees' + ANY_ASSIGNEE = 'any_assignee' + + +class TasksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_file_tasks( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Tasks: + """ + Retrieves a list of all the tasks for a file. This + + endpoint does not support pagination. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/tasks', + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Tasks) + + def create_task( + self, + item: CreateTaskItem, + *, + action: Optional[CreateTaskAction] = None, + message: Optional[str] = None, + due_at: Optional[DateTime] = None, + completion_rule: Optional[CreateTaskCompletionRule] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Task: + """ + Creates a single task on a file. This task is not assigned to any user and + + will need to be assigned separately. + + :param item: The file to attach the task to. + :type item: CreateTaskItem + :param action: The action the task assignee will be prompted to do. Must be + + * `review` defines an approval task that can be approved or, + rejected + * `complete` defines a general task which can be completed., defaults to None + :type action: Optional[CreateTaskAction], optional + :param message: An optional message to include with the task., defaults to None + :type message: Optional[str], optional + :param due_at: Defines when the task is due. Defaults to `null` if not + provided., defaults to None + :type due_at: Optional[DateTime], optional + :param completion_rule: Defines which assignees need to complete this task before the task + is considered completed. + + * `all_assignees` (default) requires all assignees to review or + approve the task in order for it to be considered completed. + * `any_assignee` accepts any one assignee to review or + approve the task in order for it to be considered completed., defaults to None + :type completion_rule: Optional[CreateTaskCompletionRule], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'item': item, + 'action': action, + 'message': message, + 'due_at': due_at, + 'completion_rule': completion_rule, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/tasks']), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Task) + + def get_task_by_id( + self, task_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Task: + """ + Retrieves information about a specific task. + :param task_id: The ID of the task. + Example: "12345" + :type task_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/tasks/', + to_string(task_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Task) + + def update_task_by_id( + self, + task_id: str, + *, + action: Optional[UpdateTaskByIdAction] = None, + message: Optional[str] = None, + due_at: Optional[DateTime] = None, + completion_rule: Optional[UpdateTaskByIdCompletionRule] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Task: + """ + Updates a task. This can be used to update a task's configuration, or to + + update its completion state. + + :param task_id: The ID of the task. + Example: "12345" + :type task_id: str + :param action: The action the task assignee will be prompted to do. Must be + + * `review` defines an approval task that can be approved or + rejected, + * `complete` defines a general task which can be completed., defaults to None + :type action: Optional[UpdateTaskByIdAction], optional + :param message: The message included with the task., defaults to None + :type message: Optional[str], optional + :param due_at: When the task is due at., defaults to None + :type due_at: Optional[DateTime], optional + :param completion_rule: Defines which assignees need to complete this task before the task + is considered completed. + + * `all_assignees` (default) requires all assignees to review or + approve the task in order for it to be considered completed. + * `any_assignee` accepts any one assignee to review or + approve the task in order for it to be considered completed., defaults to None + :type completion_rule: Optional[UpdateTaskByIdCompletionRule], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'action': action, + 'message': message, + 'due_at': due_at, + 'completion_rule': completion_rule, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/tasks/', + to_string(task_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Task) + + def delete_task_by_id( + self, task_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Removes a task from a file. + :param task_id: The ID of the task. + Example: "12345" + :type task_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/tasks/', + to_string(task_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/terms_of_service_user_statuses.py b/box_sdk_gen/managers/terms_of_service_user_statuses.py new file mode 100644 index 000000000..9374d3c21 --- /dev/null +++ b/box_sdk_gen/managers/terms_of_service_user_statuses.py @@ -0,0 +1,235 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.terms_of_service_user_statuses import ( + TermsOfServiceUserStatuses, +) + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.terms_of_service_user_status import TermsOfServiceUserStatus + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class CreateTermsOfServiceStatusForUserTosTypeField(str, Enum): + TERMS_OF_SERVICE = 'terms_of_service' + + +class CreateTermsOfServiceStatusForUserTos(BaseObject): + _discriminator = 'type', {'terms_of_service'} + + def __init__( + self, + id: str, + *, + type: CreateTermsOfServiceStatusForUserTosTypeField = CreateTermsOfServiceStatusForUserTosTypeField.TERMS_OF_SERVICE, + **kwargs + ): + """ + :param id: The ID of terms of service. + :type id: str + :param type: The type of object., defaults to CreateTermsOfServiceStatusForUserTosTypeField.TERMS_OF_SERVICE + :type type: CreateTermsOfServiceStatusForUserTosTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateTermsOfServiceStatusForUserUserTypeField(str, Enum): + USER = 'user' + + +class CreateTermsOfServiceStatusForUserUser(BaseObject): + _discriminator = 'type', {'user'} + + def __init__( + self, + id: str, + *, + type: CreateTermsOfServiceStatusForUserUserTypeField = CreateTermsOfServiceStatusForUserUserTypeField.USER, + **kwargs + ): + """ + :param id: The ID of user. + :type id: str + :param type: The type of object., defaults to CreateTermsOfServiceStatusForUserUserTypeField.USER + :type type: CreateTermsOfServiceStatusForUserUserTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class TermsOfServiceUserStatusesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_terms_of_service_user_statuses( + self, + tos_id: str, + *, + user_id: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TermsOfServiceUserStatuses: + """ + Retrieves an overview of users and their status for a + + terms of service, including Whether they have accepted + + + the terms and when. + + :param tos_id: The ID of the terms of service. + :type tos_id: str + :param user_id: Limits results to the given user ID., defaults to None + :type user_id: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'tos_id': to_string(tos_id), 'user_id': to_string(user_id)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/terms_of_service_user_statuses', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TermsOfServiceUserStatuses) + + def create_terms_of_service_status_for_user( + self, + tos: CreateTermsOfServiceStatusForUserTos, + user: CreateTermsOfServiceStatusForUserUser, + is_accepted: bool, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TermsOfServiceUserStatus: + """ + Sets the status for a terms of service for a user. + :param tos: The terms of service to set the status for. + :type tos: CreateTermsOfServiceStatusForUserTos + :param user: The user to set the status for. + :type user: CreateTermsOfServiceStatusForUserUser + :param is_accepted: Whether the user has accepted the terms. + :type is_accepted: bool + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'tos': tos, 'user': user, 'is_accepted': is_accepted} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/terms_of_service_user_statuses', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TermsOfServiceUserStatus) + + def update_terms_of_service_status_for_user_by_id( + self, + terms_of_service_user_status_id: str, + is_accepted: bool, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TermsOfServiceUserStatus: + """ + Updates the status for a terms of service for a user. + :param terms_of_service_user_status_id: The ID of the terms of service status. + Example: "324234" + :type terms_of_service_user_status_id: str + :param is_accepted: Whether the user has accepted the terms. + :type is_accepted: bool + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'is_accepted': is_accepted} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/terms_of_service_user_statuses/', + to_string(terms_of_service_user_status_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TermsOfServiceUserStatus) diff --git a/box_sdk_gen/managers/terms_of_services.py b/box_sdk_gen/managers/terms_of_services.py new file mode 100644 index 000000000..2440409f1 --- /dev/null +++ b/box_sdk_gen/managers/terms_of_services.py @@ -0,0 +1,235 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.terms_of_services import TermsOfServices + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.terms_of_service import TermsOfService + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetTermsOfServiceTosType(str, Enum): + EXTERNAL = 'external' + MANAGED = 'managed' + + +class CreateTermsOfServiceStatus(str, Enum): + ENABLED = 'enabled' + DISABLED = 'disabled' + + +class CreateTermsOfServiceTosType(str, Enum): + EXTERNAL = 'external' + MANAGED = 'managed' + + +class UpdateTermsOfServiceByIdStatus(str, Enum): + ENABLED = 'enabled' + DISABLED = 'disabled' + + +class TermsOfServicesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_terms_of_service( + self, + *, + tos_type: Optional[GetTermsOfServiceTosType] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TermsOfServices: + """ + Returns the current terms of service text and settings + + for the enterprise. + + :param tos_type: Limits the results to the terms of service of the given type., defaults to None + :type tos_type: Optional[GetTermsOfServiceTosType], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'tos_type': to_string(tos_type)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/terms_of_services'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TermsOfServices) + + def create_terms_of_service( + self, + status: CreateTermsOfServiceStatus, + text: str, + *, + tos_type: Optional[CreateTermsOfServiceTosType] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TermsOfService: + """ + Creates a terms of service for a given enterprise + + and type of user. + + :param status: Whether this terms of service is active. + :type status: CreateTermsOfServiceStatus + :param text: The terms of service text to display to users. + + The text can be set to empty if the `status` is set to `disabled`. + :type text: str + :param tos_type: The type of user to set the terms of + service for., defaults to None + :type tos_type: Optional[CreateTermsOfServiceTosType], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'status': status, 'tos_type': tos_type, 'text': text} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/terms_of_services'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TermsOfService) + + def get_terms_of_service_by_id( + self, + terms_of_service_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TermsOfService: + """ + Fetches a specific terms of service. + :param terms_of_service_id: The ID of the terms of service. + Example: "324234" + :type terms_of_service_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/terms_of_services/', + to_string(terms_of_service_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TermsOfService) + + def update_terms_of_service_by_id( + self, + terms_of_service_id: str, + status: UpdateTermsOfServiceByIdStatus, + text: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TermsOfService: + """ + Updates a specific terms of service. + :param terms_of_service_id: The ID of the terms of service. + Example: "324234" + :type terms_of_service_id: str + :param status: Whether this terms of service is active. + :type status: UpdateTermsOfServiceByIdStatus + :param text: The terms of service text to display to users. + + The text can be set to empty if the `status` is set to `disabled`. + :type text: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'status': status, 'text': text} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/terms_of_services/', + to_string(terms_of_service_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TermsOfService) diff --git a/box_sdk_gen/managers/transfer.py b/box_sdk_gen/managers/transfer.py new file mode 100644 index 000000000..866934f48 --- /dev/null +++ b/box_sdk_gen/managers/transfer.py @@ -0,0 +1,193 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class TransferOwnedFolderOwnedBy(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of the user who the folder will be + transferred to. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class TransferManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def transfer_owned_folder( + self, + user_id: str, + owned_by: TransferOwnedFolderOwnedBy, + *, + fields: Optional[List[str]] = None, + notify: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> FolderFull: + """ + Move all of the items (files, folders and workflows) owned by a user into + + another user's account + + + Only the root folder (`0`) can be transferred. + + + Folders can only be moved across users by users with administrative + + + permissions. + + + All existing shared links and folder-level collaborations are transferred + + + during the operation. Please note that while collaborations at the individual + + + file-level are transferred during the operation, the collaborations are + + + deleted when the original user is deleted. + + + If the user has a large number of items across all folders, the call will + + + be run asynchronously. If the operation is not completed within 10 minutes, + + + the user will receive a 200 OK response, and the operation will continue running. + + + If the destination path has a metadata cascade policy attached to any of + + + the parent folders, a metadata cascade operation will be kicked off + + + asynchronously. + + + There is currently no way to check for when this operation is finished. + + + The destination folder's name will be in the format `{User}'s Files and + + + Folders`, where `{User}` is the display name of the user. + + + To make this API call your application will need to have the "Read and write + + + all files and folders stored in Box" scope enabled. + + + Please make sure the destination user has access to `Relay` or `Relay Lite`, + + + and has access to the files and folders involved in the workflows being + + + transferred. + + + Admins will receive an email when the operation is completed. + + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param owned_by: The user who the folder will be transferred to. + :type owned_by: TransferOwnedFolderOwnedBy + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param notify: Determines if users should receive email notification + for the action performed., defaults to None + :type notify: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'owned_by': owned_by} + query_params_map: Dict[str, str] = prepare_params( + {'fields': to_string(fields), 'notify': to_string(notify)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + '/folders/0', + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, FolderFull) diff --git a/box_sdk_gen/managers/trashed_files.py b/box_sdk_gen/managers/trashed_files.py new file mode 100644 index 000000000..340d93b32 --- /dev/null +++ b/box_sdk_gen/managers/trashed_files.py @@ -0,0 +1,252 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.trash_file_restored import TrashFileRestored + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.trash_file import TrashFile + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class RestoreFileFromTrashParent(BaseObject): + def __init__(self, *, id: Optional[str] = None, **kwargs): + """ + :param id: The ID of parent item., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + + +class TrashedFilesManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def restore_file_from_trash( + self, + file_id: str, + *, + name: Optional[str] = None, + parent: Optional[RestoreFileFromTrashParent] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TrashFileRestored: + """ + Restores a file that has been moved to the trash. + + An optional new parent ID can be provided to restore the file to in case the + + + original folder has been deleted. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param name: An optional new name for the file., defaults to None + :type name: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name, 'parent': parent} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + ] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TrashFileRestored) + + def get_trashed_file_by_id( + self, + file_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TrashFile: + """ + Retrieves a file that has been moved to the trash. + + Please note that only if the file itself has been moved to the + + + trash can it be retrieved with this API call. If instead one of + + + its parent folders was moved to the trash, only that folder + + + can be inspected using the + + + [`GET /folders/:id/trash`](e://get_folders_id_trash) API. + + + To list all items that have been moved to the trash, please + + + use the [`GET /folders/trash/items`](e://get-folders-trash-items/) + + + API. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/trash', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TrashFile) + + def delete_trashed_file_by_id( + self, file_id: str, *, extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Permanently deletes a file that is in the trash. + + This action cannot be undone. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/files/', + to_string(file_id), + '/trash', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/trashed_folders.py b/box_sdk_gen/managers/trashed_folders.py new file mode 100644 index 000000000..b41e66dd1 --- /dev/null +++ b/box_sdk_gen/managers/trashed_folders.py @@ -0,0 +1,279 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.trash_folder_restored import TrashFolderRestored + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.trash_folder import TrashFolder + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class RestoreFolderFromTrashParent(BaseObject): + def __init__(self, *, id: Optional[str] = None, **kwargs): + """ + :param id: The ID of parent item., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + + +class TrashedFoldersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def restore_folder_from_trash( + self, + folder_id: str, + *, + name: Optional[str] = None, + parent: Optional[RestoreFolderFromTrashParent] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TrashFolderRestored: + """ + Restores a folder that has been moved to the trash. + + An optional new parent ID can be provided to restore the folder to in case the + + + original folder has been deleted. + + + During this operation, part of the file tree will be locked, mainly + + + the source folder and all of its descendants, as well as the destination + + + folder. + + + For the duration of the operation, no other move, copy, delete, or restore + + + operation can performed on any of the locked folders. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param name: An optional new name for the folder., defaults to None + :type name: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name, 'parent': parent} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + ] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TrashFolderRestored) + + def get_trashed_folder_by_id( + self, + folder_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TrashFolder: + """ + Retrieves a folder that has been moved to the trash. + + Please note that only if the folder itself has been moved to the + + + trash can it be retrieved with this API call. If instead one of + + + its parent folders was moved to the trash, only that folder + + + can be inspected using the + + + [`GET /folders/:id/trash`](e://get_folders_id_trash) API. + + + To list all items that have been moved to the trash, please + + + use the [`GET /folders/trash/items`](e://get-folders-trash-items/) + + + API. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/trash', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TrashFolder) + + def delete_trashed_folder_by_id( + self, + folder_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Permanently deletes a folder that is in the trash. + + This action cannot be undone. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + Example: "12345" + :type folder_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/', + to_string(folder_id), + '/trash', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/trashed_items.py b/box_sdk_gen/managers/trashed_items.py new file mode 100644 index 000000000..242cd2ed3 --- /dev/null +++ b/box_sdk_gen/managers/trashed_items.py @@ -0,0 +1,171 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.items import Items + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetTrashedItemsDirection(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class GetTrashedItemsSort(str, Enum): + NAME = 'name' + DATE = 'date' + SIZE = 'size' + + +class TrashedItemsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_trashed_items( + self, + *, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + usemarker: Optional[bool] = None, + marker: Optional[str] = None, + direction: Optional[GetTrashedItemsDirection] = None, + sort: Optional[GetTrashedItemsSort] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Items: + """ + Retrieves the files and folders that have been moved + + to the trash. + + + Any attribute in the full files or folders objects can be passed + + + in with the `fields` parameter to retrieve those specific + + + attributes that are not returned by default. + + + This endpoint defaults to use offset-based pagination, yet also supports + + + marker-based pagination using the `marker` parameter. + + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param usemarker: Specifies whether to use marker-based pagination instead of + offset-based pagination. Only one pagination method can + be used at a time. + + By setting this value to true, the API will return a `marker` field + that can be passed as a parameter to this endpoint to get the next + page of the response., defaults to None + :type usemarker: Optional[bool], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param direction: The direction to sort results in. This can be either in alphabetical ascending + (`ASC`) or descending (`DESC`) order., defaults to None + :type direction: Optional[GetTrashedItemsDirection], optional + :param sort: Defines the **second** attribute by which items + are sorted. + + Items are always sorted by their `type` first, with + folders listed before files, and files listed + before web links. + + This parameter is not supported when using marker-based pagination., defaults to None + :type sort: Optional[GetTrashedItemsSort], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'fields': to_string(fields), + 'limit': to_string(limit), + 'offset': to_string(offset), + 'usemarker': to_string(usemarker), + 'marker': to_string(marker), + 'direction': to_string(direction), + 'sort': to_string(sort), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/folders/trash/items', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Items) diff --git a/box_sdk_gen/managers/trashed_web_links.py b/box_sdk_gen/managers/trashed_web_links.py new file mode 100644 index 000000000..de1cbe168 --- /dev/null +++ b/box_sdk_gen/managers/trashed_web_links.py @@ -0,0 +1,213 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.trash_web_link_restored import TrashWebLinkRestored + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.trash_web_link import TrashWebLink + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class RestoreWeblinkFromTrashParent(BaseObject): + def __init__(self, *, id: Optional[str] = None, **kwargs): + """ + :param id: The ID of parent item., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + + +class TrashedWebLinksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def restore_weblink_from_trash( + self, + web_link_id: str, + *, + name: Optional[str] = None, + parent: Optional[RestoreWeblinkFromTrashParent] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TrashWebLinkRestored: + """ + Restores a web link that has been moved to the trash. + + An optional new parent ID can be provided to restore the web link to in case + + + the original folder has been deleted. + + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param name: An optional new name for the web link., defaults to None + :type name: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name, 'parent': parent} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + ] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TrashWebLinkRestored) + + def get_trashed_web_link_by_id( + self, + web_link_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> TrashWebLink: + """ + Retrieves a web link that has been moved to the trash. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + '/trash', + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, TrashWebLink) + + def delete_trashed_web_link_by_id( + self, + web_link_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Permanently deletes a web link that is in the trash. + + This action cannot be undone. + + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + '/trash', + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/uploads.py b/box_sdk_gen/managers/uploads.py new file mode 100644 index 000000000..1bdb269a6 --- /dev/null +++ b/box_sdk_gen/managers/uploads.py @@ -0,0 +1,546 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.files import Files + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.upload_url import UploadUrl + +from box_sdk_gen.schemas.conflict_error import ConflictError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.networking.fetch_options import MultipartItem + +from box_sdk_gen.serialization.json import SerializedData + + +class UploadFileVersionAttributes(BaseObject): + def __init__( + self, name: str, *, content_modified_at: Optional[DateTime] = None, **kwargs + ): + """ + :param name: An optional new name for the file. If specified, the file + will be renamed when the new version is uploaded. + :type name: str + :param content_modified_at: Defines the time the file was last modified at. + + If not set, the upload time will be used., defaults to None + :type content_modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.name = name + self.content_modified_at = content_modified_at + + +class PreflightFileUploadCheckParent(BaseObject): + def __init__(self, *, id: Optional[str] = None, **kwargs): + """ + :param id: The ID of parent item., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + + +class UploadFileAttributesParentField(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The id of the parent folder. Use + `0` for the user's root folder. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class UploadFileAttributes(BaseObject): + def __init__( + self, + name: str, + parent: UploadFileAttributesParentField, + *, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param name: The name of the file. + + File names must be unique within their parent folder. The name check is case-insensitive, so a file + named `New File` cannot be created in a parent folder that already contains a folder named `new file`. + :type name: str + :param parent: The parent folder to upload the file to. + :type parent: UploadFileAttributesParentField + :param content_created_at: Defines the time the file was originally created at. + + If not set, the upload time will be used., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: Defines the time the file was last modified at. + + If not set, the upload time will be used., defaults to None + :type content_modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.name = name + self.parent = parent + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + + +class UploadWithPreflightCheckAttributesParentField(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The id of the parent folder. Use + `0` for the user's root folder. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class UploadWithPreflightCheckAttributes(BaseObject): + def __init__( + self, + name: str, + parent: UploadWithPreflightCheckAttributesParentField, + size: int, + *, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param name: The name of the file. + + File names must be unique within their parent folder. The name check is case-insensitive, so a file + named `New File` cannot be created in a parent folder that already contains a folder named `new file`. + :type name: str + :param parent: The parent folder to upload the file to. + :type parent: UploadWithPreflightCheckAttributesParentField + :param size: The size of the file in bytes + :type size: int + :param content_created_at: Defines the time the file was originally created at. + + If not set, the upload time will be used., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: Defines the time the file was last modified at. + + If not set, the upload time will be used., defaults to None + :type content_modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.name = name + self.parent = parent + self.size = size + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + + +class UploadsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def upload_file_version( + self, + file_id: str, + attributes: UploadFileVersionAttributes, + file: ByteStream, + *, + file_file_name: Optional[str] = None, + file_content_type: Optional[str] = None, + fields: Optional[List[str]] = None, + if_match: Optional[str] = None, + content_md_5: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Files: + """ + Update a file's content. For file sizes over 50MB we recommend + + using the Chunk Upload APIs. + + + The `attributes` part of the body must come **before** the + + + `file` part. Requests that do not follow this format when + + + uploading the file will receive a HTTP `400` error with a + + + `metadata_after_file_contents` error code. + + :param file_id: The unique identifier that represents a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + Example: "12345" + :type file_id: str + :param attributes: The additional attributes of the file being uploaded. Mainly the + name and the parent folder. These attributes are part of the multi + part request body and are in JSON format. + + + + The `attributes` part of the body must come **before** the + `file` part. Requests that do not follow this format when + uploading the file will receive a HTTP `400` error with a + `metadata_after_file_contents` error code. + + + :type attributes: UploadFileVersionAttributes + :param file: The content of the file to upload to Box. + + + + The `attributes` part of the body must come **before** the + `file` part. Requests that do not follow this format when + uploading the file will receive a HTTP `400` error with a + `metadata_after_file_contents` error code. + + + :type file: ByteStream + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param if_match: Ensures this item hasn't recently changed before + making changes. + + Pass in the item's last observed `etag` value + into this header and the endpoint will fail + with a `412 Precondition Failed` if it + has changed since., defaults to None + :type if_match: Optional[str], optional + :param content_md_5: An optional header containing the SHA1 hash of the file to + ensure that the file was not corrupted in transit., defaults to None + :type content_md_5: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'attributes': attributes, + 'file': file, + 'file_file_name': file_file_name, + 'file_content_type': file_content_type, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + { + 'if-match': to_string(if_match), + 'content-md5': to_string(content_md_5), + **extra_headers, + } + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.upload_url, + '/2.0/files/', + to_string(file_id), + '/content', + ] + ), + method='POST', + params=query_params_map, + headers=headers_map, + multipart_data=[ + MultipartItem(part_name='attributes', data=serialize(attributes)), + MultipartItem( + part_name='file', + file_stream=file, + file_name=file_file_name, + content_type=file_content_type, + ), + ], + content_type='multipart/form-data', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Files) + + def preflight_file_upload_check( + self, + *, + name: Optional[str] = None, + size: Optional[int] = None, + parent: Optional[PreflightFileUploadCheckParent] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UploadUrl: + """ + Performs a check to verify that a file will be accepted by Box + + before you upload the entire file. + + :param name: The name for the file., defaults to None + :type name: Optional[str], optional + :param size: The size of the file in bytes., defaults to None + :type size: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'name': name, 'size': size, 'parent': parent} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/files/content'] + ), + method='OPTIONS', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UploadUrl) + + def upload_file( + self, + attributes: UploadFileAttributes, + file: ByteStream, + *, + file_file_name: Optional[str] = None, + file_content_type: Optional[str] = None, + fields: Optional[List[str]] = None, + content_md_5: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Files: + """ + Uploads a small file to Box. For file sizes over 50MB we recommend + + using the Chunk Upload APIs. + + + The `attributes` part of the body must come **before** the + + + `file` part. Requests that do not follow this format when + + + uploading the file will receive a HTTP `400` error with a + + + `metadata_after_file_contents` error code. + + :param attributes: The additional attributes of the file being uploaded. Mainly the + name and the parent folder. These attributes are part of the multi + part request body and are in JSON format. + + + + The `attributes` part of the body must come **before** the + `file` part. Requests that do not follow this format when + uploading the file will receive a HTTP `400` error with a + `metadata_after_file_contents` error code. + + + :type attributes: UploadFileAttributes + :param file: The content of the file to upload to Box. + + + + The `attributes` part of the body must come **before** the + `file` part. Requests that do not follow this format when + uploading the file will receive a HTTP `400` error with a + `metadata_after_file_contents` error code. + + + :type file: ByteStream + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param content_md_5: An optional header containing the SHA1 hash of the file to + ensure that the file was not corrupted in transit., defaults to None + :type content_md_5: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'attributes': attributes, + 'file': file, + 'file_file_name': file_file_name, + 'file_content_type': file_content_type, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + {'content-md5': to_string(content_md_5), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.upload_url, '/2.0/files/content'] + ), + method='POST', + params=query_params_map, + headers=headers_map, + multipart_data=[ + MultipartItem(part_name='attributes', data=serialize(attributes)), + MultipartItem( + part_name='file', + file_stream=file, + file_name=file_file_name, + content_type=file_content_type, + ), + ], + content_type='multipart/form-data', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Files) + + def upload_with_preflight_check( + self, + attributes: UploadWithPreflightCheckAttributes, + file: ByteStream, + *, + file_file_name: Optional[str] = None, + file_content_type: Optional[str] = None, + fields: Optional[List[str]] = None, + content_md_5: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Files: + """ + Upload a file with a preflight check + :param file: The content of the file to upload to Box. + + + + The `attributes` part of the body must come **before** the + `file` part. Requests that do not follow this format when + uploading the file will receive a HTTP `400` error with a + `metadata_after_file_contents` error code. + + + :type file: ByteStream + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param content_md_5: An optional header containing the SHA1 hash of the file to + ensure that the file was not corrupted in transit., defaults to None + :type content_md_5: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'attributes': attributes, + 'file': file, + 'file_file_name': file_file_name, + 'file_content_type': file_content_type, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params( + {'content-md5': to_string(content_md_5), **extra_headers} + ) + preflight_upload_url: UploadUrl = self.preflight_file_upload_check( + name=attributes.name, + size=attributes.size, + parent=PreflightFileUploadCheckParent(id=attributes.parent.id), + extra_headers=extra_headers, + ) + if ( + preflight_upload_url.upload_url == None + or not 'http' in preflight_upload_url.upload_url + ): + raise BoxSDKError(message='Unable to get preflight upload URL') + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=preflight_upload_url.upload_url, + method='POST', + params=query_params_map, + headers=headers_map, + multipart_data=[ + MultipartItem(part_name='attributes', data=serialize(attributes)), + MultipartItem( + part_name='file', + file_stream=file, + file_name=file_file_name, + content_type=file_content_type, + ), + ], + content_type='multipart/form-data', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Files) diff --git a/box_sdk_gen/managers/user_collaborations.py b/box_sdk_gen/managers/user_collaborations.py new file mode 100644 index 000000000..10641f062 --- /dev/null +++ b/box_sdk_gen/managers/user_collaborations.py @@ -0,0 +1,444 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.collaboration import Collaboration + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import DateTime + + +class UpdateCollaborationByIdRole(str, Enum): + EDITOR = 'editor' + VIEWER = 'viewer' + PREVIEWER = 'previewer' + UPLOADER = 'uploader' + PREVIEWER_UPLOADER = 'previewer uploader' + VIEWER_UPLOADER = 'viewer uploader' + CO_OWNER = 'co-owner' + OWNER = 'owner' + + +class UpdateCollaborationByIdStatus(str, Enum): + PENDING = 'pending' + ACCEPTED = 'accepted' + REJECTED = 'rejected' + + +class CreateCollaborationItemTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class CreateCollaborationItem(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__( + self, + *, + type: Optional[CreateCollaborationItemTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of the item that this collaboration will be + granted access to., defaults to None + :type type: Optional[CreateCollaborationItemTypeField], optional + :param id: The ID of the item that will be granted access to., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class CreateCollaborationAccessibleByTypeField(str, Enum): + USER = 'user' + GROUP = 'group' + + +class CreateCollaborationAccessibleBy(BaseObject): + _discriminator = 'type', {'user', 'group'} + + def __init__( + self, + type: CreateCollaborationAccessibleByTypeField, + *, + id: Optional[str] = None, + login: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of collaborator to invite. + :type type: CreateCollaborationAccessibleByTypeField + :param id: The ID of the user or group. + + Alternatively, use `login` to specify a user by email + address., defaults to None + :type id: Optional[str], optional + :param login: The email address of the user to grant access to the item. + + Alternatively, use `id` to specify a user by user ID., defaults to None + :type login: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.login = login + + +class CreateCollaborationRole(str, Enum): + EDITOR = 'editor' + VIEWER = 'viewer' + PREVIEWER = 'previewer' + UPLOADER = 'uploader' + PREVIEWER_UPLOADER = 'previewer uploader' + VIEWER_UPLOADER = 'viewer uploader' + CO_OWNER = 'co-owner' + + +class UserCollaborationsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_collaboration_by_id( + self, + collaboration_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Collaboration: + """ + Retrieves a single collaboration. + :param collaboration_id: The ID of the collaboration. + Example: "1234" + :type collaboration_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaborations/', + to_string(collaboration_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Collaboration) + + def update_collaboration_by_id( + self, + collaboration_id: str, + role: UpdateCollaborationByIdRole, + *, + status: Optional[UpdateCollaborationByIdStatus] = None, + expires_at: Optional[DateTime] = None, + can_view_path: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Optional[Collaboration]: + """ + Updates a collaboration. + + Can be used to change the owner of an item, or to + + + accept collaboration invites. + + :param collaboration_id: The ID of the collaboration. + Example: "1234" + :type collaboration_id: str + :param role: The level of access granted. + :type role: UpdateCollaborationByIdRole + :param status: Set the status of a `pending` collaboration invitation, + effectively accepting, or rejecting the invite., defaults to None + :type status: Optional[UpdateCollaborationByIdStatus], optional + :param expires_at: Update the expiration date for the collaboration. At this date, + the collaboration will be automatically removed from the item. + + This feature will only work if the **Automatically remove invited + collaborators: Allow folder owners to extend the expiry date** + setting has been enabled in the **Enterprise Settings** + of the **Admin Console**. When the setting is not enabled, + collaborations can not have an expiry date and a value for this + field will be result in an error. + + Additionally, a collaboration can only be given an + expiration if it was created after the **Automatically remove + invited collaborator** setting was enabled., defaults to None + :type expires_at: Optional[DateTime], optional + :param can_view_path: Determines if the invited users can see the entire parent path to + the associated folder. The user will not gain privileges in any + parent folder and therefore can not see content the user is not + collaborated on. + + Be aware that this meaningfully increases the time required to load the + invitee's **All Files** page. We recommend you limit the number of + collaborations with `can_view_path` enabled to 1,000 per user. + + Only owner or co-owners can invite collaborators with a `can_view_path` of + `true`. + + `can_view_path` can only be used for folder collaborations., defaults to None + :type can_view_path: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'role': role, + 'status': status, + 'expires_at': expires_at, + 'can_view_path': can_view_path, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaborations/', + to_string(collaboration_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + if to_string(response.status) == '204': + return None + return deserialize(response.data, Collaboration) + + def delete_collaboration_by_id( + self, + collaboration_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a single collaboration. + :param collaboration_id: The ID of the collaboration. + Example: "1234" + :type collaboration_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/collaborations/', + to_string(collaboration_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + def create_collaboration( + self, + item: CreateCollaborationItem, + accessible_by: CreateCollaborationAccessibleBy, + role: CreateCollaborationRole, + *, + is_access_only: Optional[bool] = None, + can_view_path: Optional[bool] = None, + expires_at: Optional[DateTime] = None, + fields: Optional[List[str]] = None, + notify: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Collaboration: + """ + Adds a collaboration for a single user or a single group to a file + + or folder. + + + Collaborations can be created using email address, user IDs, or a + + + group IDs. + + + If a collaboration is being created with a group, access to + + + this endpoint is dependent on the group's ability to be invited. + + + If collaboration is in `pending` status, the following fields + + + are redacted: + + + - `login` and `name` are hidden if a collaboration was created + + + using `user_id`, + + + - `name` is hidden if a collaboration was created using `login`. + + :param item: The item to attach the comment to. + :type item: CreateCollaborationItem + :param accessible_by: The user or group to give access to the item. + :type accessible_by: CreateCollaborationAccessibleBy + :param role: The level of access granted. + :type role: CreateCollaborationRole + :param is_access_only: If set to `true`, collaborators have access to + shared items, but such items won't be visible in the + All Files list. Additionally, collaborators won't + see the path to the root folder for the + shared item., defaults to None + :type is_access_only: Optional[bool], optional + :param can_view_path: Determines if the invited users can see the entire parent path to + the associated folder. The user will not gain privileges in any + parent folder and therefore can not see content the user is not + collaborated on. + + Be aware that this meaningfully increases the time required to load the + invitee's **All Files** page. We recommend you limit the number of + collaborations with `can_view_path` enabled to 1,000 per user. + + Only owner or co-owners can invite collaborators with a `can_view_path` of + `true`. + + `can_view_path` can only be used for folder collaborations., defaults to None + :type can_view_path: Optional[bool], optional + :param expires_at: Set the expiration date for the collaboration. At this date, the + collaboration will be automatically removed from the item. + + This feature will only work if the **Automatically remove invited + collaborators: Allow folder owners to extend the expiry date** + setting has been enabled in the **Enterprise Settings** + of the **Admin Console**. When the setting is not enabled, + collaborations can not have an expiry date and a value for this + field will be result in an error., defaults to None + :type expires_at: Optional[DateTime], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param notify: Determines if users should receive email notification + for the action performed., defaults to None + :type notify: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'item': item, + 'accessible_by': accessible_by, + 'role': role, + 'is_access_only': is_access_only, + 'can_view_path': can_view_path, + 'expires_at': expires_at, + } + query_params_map: Dict[str, str] = prepare_params( + {'fields': to_string(fields), 'notify': to_string(notify)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/collaborations'] + ), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Collaboration) diff --git a/box_sdk_gen/managers/users.py b/box_sdk_gen/managers/users.py new file mode 100644 index 000000000..52df23cb1 --- /dev/null +++ b/box_sdk_gen/managers/users.py @@ -0,0 +1,701 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.internal.null_value import NullValue + +from typing import Union + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.users import Users + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.user_full import UserFull + +from box_sdk_gen.schemas.tracking_code import TrackingCode + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class GetUsersUserType(str, Enum): + ALL = 'all' + MANAGED = 'managed' + EXTERNAL = 'external' + + +class CreateUserRole(str, Enum): + COADMIN = 'coadmin' + USER = 'user' + + +class CreateUserStatus(str, Enum): + ACTIVE = 'active' + INACTIVE = 'inactive' + CANNOT_DELETE_EDIT = 'cannot_delete_edit' + CANNOT_DELETE_EDIT_UPLOAD = 'cannot_delete_edit_upload' + + +class UpdateUserByIdRole(str, Enum): + COADMIN = 'coadmin' + USER = 'user' + + +class UpdateUserByIdStatus(str, Enum): + ACTIVE = 'active' + INACTIVE = 'inactive' + CANNOT_DELETE_EDIT = 'cannot_delete_edit' + CANNOT_DELETE_EDIT_UPLOAD = 'cannot_delete_edit_upload' + + +class UpdateUserByIdNotificationEmail(BaseObject): + def __init__(self, *, email: Optional[str] = None, **kwargs): + """ + :param email: The email address to send the notifications to., defaults to None + :type email: Optional[str], optional + """ + super().__init__(**kwargs) + self.email = email + + +class UsersManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_users( + self, + *, + filter_term: Optional[str] = None, + user_type: Optional[GetUsersUserType] = None, + external_app_user_id: Optional[str] = None, + fields: Optional[List[str]] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + usemarker: Optional[bool] = None, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Users: + """ + Returns a list of all users for the Enterprise along with their `user_id`, + + `public_name`, and `login`. + + + The application and the authenticated user need to + + + have the permission to look up users in the entire + + + enterprise. + + :param filter_term: Limits the results to only users who's `name` or + `login` start with the search term. + + For externally managed users, the search term needs + to completely match the in order to find the user, and + it will only return one user at a time., defaults to None + :type filter_term: Optional[str], optional + :param user_type: Limits the results to the kind of user specified. + + * `all` returns every kind of user for whom the + `login` or `name` partially matches the + `filter_term`. It will only return an external user + if the login matches the `filter_term` completely, + and in that case it will only return that user. + * `managed` returns all managed and app users for whom + the `login` or `name` partially matches the + `filter_term`. + * `external` returns all external users for whom the + `login` matches the `filter_term` exactly., defaults to None + :type user_type: Optional[GetUsersUserType], optional + :param external_app_user_id: Limits the results to app users with the given + `external_app_user_id` value. + + When creating an app user, an + `external_app_user_id` value can be set. This value can + then be used in this endpoint to find any users that + match that `external_app_user_id` value., defaults to None + :type external_app_user_id: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param offset: The offset of the item at which to begin the response. + + Queries with offset parameter value + exceeding 10000 will be rejected + with a 400 response., defaults to None + :type offset: Optional[int], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param usemarker: Specifies whether to use marker-based pagination instead of + offset-based pagination. Only one pagination method can + be used at a time. + + By setting this value to true, the API will return a `marker` field + that can be passed as a parameter to this endpoint to get the next + page of the response., defaults to None + :type usemarker: Optional[bool], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'filter_term': to_string(filter_term), + 'user_type': to_string(user_type), + 'external_app_user_id': to_string(external_app_user_id), + 'fields': to_string(fields), + 'offset': to_string(offset), + 'limit': to_string(limit), + 'usemarker': to_string(usemarker), + 'marker': to_string(marker), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/users']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Users) + + def create_user( + self, + name: str, + *, + login: Optional[str] = None, + is_platform_access_only: Optional[bool] = None, + role: Optional[CreateUserRole] = None, + language: Optional[str] = None, + is_sync_enabled: Optional[bool] = None, + job_title: Optional[str] = None, + phone: Optional[str] = None, + address: Optional[str] = None, + space_amount: Optional[int] = None, + tracking_codes: Optional[List[TrackingCode]] = None, + can_see_managed_users: Optional[bool] = None, + timezone: Optional[str] = None, + is_external_collab_restricted: Optional[bool] = None, + is_exempt_from_device_limits: Optional[bool] = None, + is_exempt_from_login_verification: Optional[bool] = None, + status: Optional[CreateUserStatus] = None, + external_app_user_id: Optional[str] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UserFull: + """ + Creates a new managed user in an enterprise. This endpoint + + is only available to users and applications with the right + + + admin permissions. + + :param name: The name of the user. + :type name: str + :param login: The email address the user uses to log in + + Required, unless `is_platform_access_only` + is set to `true`., defaults to None + :type login: Optional[str], optional + :param is_platform_access_only: Specifies that the user is an app user., defaults to None + :type is_platform_access_only: Optional[bool], optional + :param role: The user’s enterprise role., defaults to None + :type role: Optional[CreateUserRole], optional + :param language: The language of the user, formatted in modified version of the + [ISO 639-1](/guides/api-calls/language-codes) format., defaults to None + :type language: Optional[str], optional + :param is_sync_enabled: Whether the user can use Box Sync., defaults to None + :type is_sync_enabled: Optional[bool], optional + :param job_title: The user’s job title., defaults to None + :type job_title: Optional[str], optional + :param phone: The user’s phone number., defaults to None + :type phone: Optional[str], optional + :param address: The user’s address., defaults to None + :type address: Optional[str], optional + :param space_amount: The user’s total available space in bytes. Set this to `-1` to + indicate unlimited storage., defaults to None + :type space_amount: Optional[int], optional + :param tracking_codes: Tracking codes allow an admin to generate reports from the + admin console and assign an attribute to a specific group + of users. This setting must be enabled for an enterprise before it + can be used., defaults to None + :type tracking_codes: Optional[List[TrackingCode]], optional + :param can_see_managed_users: Whether the user can see other enterprise users in their + contact list., defaults to None + :type can_see_managed_users: Optional[bool], optional + :param timezone: The user's timezone., defaults to None + :type timezone: Optional[str], optional + :param is_external_collab_restricted: Whether the user is allowed to collaborate with users outside + their enterprise., defaults to None + :type is_external_collab_restricted: Optional[bool], optional + :param is_exempt_from_device_limits: Whether to exempt the user from enterprise device limits., defaults to None + :type is_exempt_from_device_limits: Optional[bool], optional + :param is_exempt_from_login_verification: Whether the user must use two-factor authentication., defaults to None + :type is_exempt_from_login_verification: Optional[bool], optional + :param status: The user's account status., defaults to None + :type status: Optional[CreateUserStatus], optional + :param external_app_user_id: An external identifier for an app user, which can be used to look + up the user. This can be used to tie user IDs from external + identity providers to Box users., defaults to None + :type external_app_user_id: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'name': name, + 'login': login, + 'is_platform_access_only': is_platform_access_only, + 'role': role, + 'language': language, + 'is_sync_enabled': is_sync_enabled, + 'job_title': job_title, + 'phone': phone, + 'address': address, + 'space_amount': space_amount, + 'tracking_codes': tracking_codes, + 'can_see_managed_users': can_see_managed_users, + 'timezone': timezone, + 'is_external_collab_restricted': is_external_collab_restricted, + 'is_exempt_from_device_limits': is_exempt_from_device_limits, + 'is_exempt_from_login_verification': is_exempt_from_login_verification, + 'status': status, + 'external_app_user_id': external_app_user_id, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/users']), + method='POST', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UserFull) + + def get_user_me( + self, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UserFull: + """ + Retrieves information about the user who is currently authenticated. + + In the case of a client-side authenticated OAuth 2.0 application + + + this will be the user who authorized the app. + + + In the case of a JWT, server-side authenticated application + + + this will be the service account that belongs to the application + + + by default. + + + Use the `As-User` header to change who this API call is made on behalf of. + + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/users/me']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UserFull) + + def get_user_by_id( + self, + user_id: str, + *, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UserFull: + """ + Retrieves information about a user in the enterprise. + + The application and the authenticated user need to + + + have the permission to look up users in the entire + + + enterprise. + + + This endpoint also returns a limited set of information + + + for external users who are collaborated on content + + + owned by the enterprise for authenticated users with the + + + right scopes. In this case, disallowed fields will return + + + null instead. + + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + ] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UserFull) + + def update_user_by_id( + self, + user_id: str, + *, + enterprise: Union[Optional[str], NullValue] = None, + notify: Optional[bool] = None, + name: Optional[str] = None, + login: Optional[str] = None, + role: Optional[UpdateUserByIdRole] = None, + language: Optional[str] = None, + is_sync_enabled: Optional[bool] = None, + job_title: Optional[str] = None, + phone: Optional[str] = None, + address: Optional[str] = None, + tracking_codes: Optional[List[TrackingCode]] = None, + can_see_managed_users: Optional[bool] = None, + timezone: Optional[str] = None, + is_external_collab_restricted: Optional[bool] = None, + is_exempt_from_device_limits: Optional[bool] = None, + is_exempt_from_login_verification: Optional[bool] = None, + is_password_reset_required: Optional[bool] = None, + status: Optional[UpdateUserByIdStatus] = None, + space_amount: Optional[int] = None, + notification_email: Union[ + Optional[UpdateUserByIdNotificationEmail], NullValue + ] = None, + external_app_user_id: Optional[str] = None, + fields: Optional[List[str]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> UserFull: + """ + Updates a managed or app user in an enterprise. This endpoint + + is only available to users and applications with the right + + + admin permissions. + + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param enterprise: Set this to `null` to roll the user out of the enterprise + and make them a free user., defaults to None + :type enterprise: Union[Optional[str], NullValue], optional + :param notify: Whether the user should receive an email when they + are rolled out of an enterprise., defaults to None + :type notify: Optional[bool], optional + :param name: The name of the user., defaults to None + :type name: Optional[str], optional + :param login: The email address the user uses to log in + + Note: If the target user's email is not confirmed, then the + primary login address cannot be changed., defaults to None + :type login: Optional[str], optional + :param role: The user’s enterprise role., defaults to None + :type role: Optional[UpdateUserByIdRole], optional + :param language: The language of the user, formatted in modified version of the + [ISO 639-1](/guides/api-calls/language-codes) format., defaults to None + :type language: Optional[str], optional + :param is_sync_enabled: Whether the user can use Box Sync., defaults to None + :type is_sync_enabled: Optional[bool], optional + :param job_title: The user’s job title., defaults to None + :type job_title: Optional[str], optional + :param phone: The user’s phone number., defaults to None + :type phone: Optional[str], optional + :param address: The user’s address., defaults to None + :type address: Optional[str], optional + :param tracking_codes: Tracking codes allow an admin to generate reports from the + admin console and assign an attribute to a specific group + of users. This setting must be enabled for an enterprise before it + can be used., defaults to None + :type tracking_codes: Optional[List[TrackingCode]], optional + :param can_see_managed_users: Whether the user can see other enterprise users in their + contact list., defaults to None + :type can_see_managed_users: Optional[bool], optional + :param timezone: The user's timezone., defaults to None + :type timezone: Optional[str], optional + :param is_external_collab_restricted: Whether the user is allowed to collaborate with users outside + their enterprise., defaults to None + :type is_external_collab_restricted: Optional[bool], optional + :param is_exempt_from_device_limits: Whether to exempt the user from enterprise device limits., defaults to None + :type is_exempt_from_device_limits: Optional[bool], optional + :param is_exempt_from_login_verification: Whether the user must use two-factor authentication., defaults to None + :type is_exempt_from_login_verification: Optional[bool], optional + :param is_password_reset_required: Whether the user is required to reset their password., defaults to None + :type is_password_reset_required: Optional[bool], optional + :param status: The user's account status., defaults to None + :type status: Optional[UpdateUserByIdStatus], optional + :param space_amount: The user’s total available space in bytes. Set this to `-1` to + indicate unlimited storage., defaults to None + :type space_amount: Optional[int], optional + :param notification_email: An alternate notification email address to which email + notifications are sent. When it's confirmed, this will be + the email address to which notifications are sent instead of + to the primary email address. + + Set this value to `null` to remove the notification email., defaults to None + :type notification_email: Union[Optional[UpdateUserByIdNotificationEmail], NullValue], optional + :param external_app_user_id: An external identifier for an app user, which can be used to look + up the user. This can be used to tie user IDs from external + identity providers to Box users. + + Note: In order to update this field, you need to request a token + using the application that created the app user., defaults to None + :type external_app_user_id: Optional[str], optional + :param fields: A comma-separated list of attributes to include in the + response. This can be used to request fields that are + not normally returned in a standard response. + + Be aware that specifying this parameter will have the + effect that none of the standard fields are returned in + the response unless explicitly specified, instead only + fields for the mini representation are returned, additional + to the fields requested., defaults to None + :type fields: Optional[List[str]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'enterprise': enterprise, + 'notify': notify, + 'name': name, + 'login': login, + 'role': role, + 'language': language, + 'is_sync_enabled': is_sync_enabled, + 'job_title': job_title, + 'phone': phone, + 'address': address, + 'tracking_codes': tracking_codes, + 'can_see_managed_users': can_see_managed_users, + 'timezone': timezone, + 'is_external_collab_restricted': is_external_collab_restricted, + 'is_exempt_from_device_limits': is_exempt_from_device_limits, + 'is_exempt_from_login_verification': is_exempt_from_login_verification, + 'is_password_reset_required': is_password_reset_required, + 'status': status, + 'space_amount': space_amount, + 'notification_email': notification_email, + 'external_app_user_id': external_app_user_id, + } + query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)}) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + ] + ), + method='PUT', + params=query_params_map, + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, UserFull) + + def delete_user_by_id( + self, + user_id: str, + *, + notify: Optional[bool] = None, + force: Optional[bool] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a user. By default this will fail if the user + + still owns any content. Move their owned content first + + + before proceeding, or use the `force` field to delete + + + the user and their files. + + :param user_id: The ID of the user. + Example: "12345" + :type user_id: str + :param notify: Whether the user will receive email notification of + the deletion., defaults to None + :type notify: Optional[bool], optional + :param force: Whether the user should be deleted even if this user + still own files., defaults to None + :type force: Optional[bool], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'notify': to_string(notify), 'force': to_string(force)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/users/', + to_string(user_id), + ] + ), + method='DELETE', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/web_links.py b/box_sdk_gen/managers/web_links.py new file mode 100644 index 000000000..1ee5165c2 --- /dev/null +++ b/box_sdk_gen/managers/web_links.py @@ -0,0 +1,326 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from enum import Enum + +from typing import Dict + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.internal.utils import DateTime + + +class CreateWebLinkParent(BaseObject): + def __init__(self, id: str, **kwargs): + """ + :param id: The ID of parent folder. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + + +class UpdateWebLinkByIdParent(BaseObject): + def __init__( + self, *, id: Optional[str] = None, user_id: Optional[str] = None, **kwargs + ): + """ + :param id: The ID of parent item., defaults to None + :type id: Optional[str], optional + :param user_id: The input for `user_id` is optional. Moving to non-root folder is not allowed when `user_id` is present. Parent folder id should be zero when `user_id` is provided., defaults to None + :type user_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.user_id = user_id + + +class UpdateWebLinkByIdSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class UpdateWebLinkByIdSharedLink(BaseObject): + def __init__( + self, + *, + access: Optional[UpdateWebLinkByIdSharedLinkAccessField] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + unshared_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param access: The level of access for the shared link. This can be + restricted to anyone with the link (`open`), only people + within the company (`company`) and only those who + have been invited to the folder (`collaborators`). + + If not set, this field defaults to the access level specified + by the enterprise admin. To create a shared link with this + default setting pass the `shared_link` object with + no `access` field, for example `{ "shared_link": {} }`. + + The `company` access level is only available to paid + accounts., defaults to None + :type access: Optional[UpdateWebLinkByIdSharedLinkAccessField], optional + :param password: The password required to access the shared link. Set the + password to `null` to remove it. + Passwords must now be at least eight characters + long and include a number, upper case letter, or + a non-numeric or non-alphabetic character. + A password can only be set when `access` is set to `open`., defaults to None + :type password: Optional[str], optional + :param vanity_name: Defines a custom vanity name to use in the shared link URL, + for example `https://app.box.com/v/my-shared-link`. + + Custom URLs should not be used when sharing sensitive content + as vanity URLs are a lot easier to guess than regular shared + links., defaults to None + :type vanity_name: Optional[str], optional + :param unshared_at: The timestamp at which this shared link will + expire. This field can only be set by + users with paid accounts. The value must be greater than the + current date and time., defaults to None + :type unshared_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.access = access + self.password = password + self.vanity_name = vanity_name + self.unshared_at = unshared_at + + +class WebLinksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def create_web_link( + self, + url: str, + parent: CreateWebLinkParent, + *, + name: Optional[str] = None, + description: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Creates a web link object within a folder. + :param url: The URL that this web link links to. Must start with + `"http://"` or `"https://"`. + :type url: str + :param parent: The parent folder to create the web link within. + :type parent: CreateWebLinkParent + :param name: Name of the web link. Defaults to the URL if not set., defaults to None + :type name: Optional[str], optional + :param description: Description of the web link., defaults to None + :type description: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'url': url, + 'parent': parent, + 'name': name, + 'description': description, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/web_links'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) + + def get_web_link_by_id( + self, + web_link_id: str, + *, + boxapi: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Retrieve information about a web link. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param boxapi: The URL, and optional password, for the shared link of this item. + + This header can be used to access items that have not been + explicitly shared with a user. + + Use the format `shared_link=[link]` or if a password is required then + use `shared_link=[link]&shared_link_password=[password]`. + + This header can be used on the file or folder shared, as well as on any files + or folders nested within the item., defaults to None + :type boxapi: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params( + {'boxapi': to_string(boxapi), **extra_headers} + ) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) + + def update_web_link_by_id( + self, + web_link_id: str, + *, + url: Optional[str] = None, + parent: Optional[UpdateWebLinkByIdParent] = None, + name: Optional[str] = None, + description: Optional[str] = None, + shared_link: Optional[UpdateWebLinkByIdSharedLink] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> WebLink: + """ + Updates a web link object. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param url: The new URL that the web link links to. Must start with + `"http://"` or `"https://"`., defaults to None + :type url: Optional[str], optional + :param name: A new name for the web link. Defaults to the URL if not set., defaults to None + :type name: Optional[str], optional + :param description: A new description of the web link., defaults to None + :type description: Optional[str], optional + :param shared_link: The settings for the shared link to update., defaults to None + :type shared_link: Optional[UpdateWebLinkByIdSharedLink], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'url': url, + 'parent': parent, + 'name': name, + 'description': description, + 'shared_link': shared_link, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, WebLink) + + def delete_web_link_by_id( + self, + web_link_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a web link. + :param web_link_id: The ID of the web link. + Example: "12345" + :type web_link_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/web_links/', + to_string(web_link_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/webhooks.py b/box_sdk_gen/managers/webhooks.py new file mode 100644 index 000000000..27f3e3a17 --- /dev/null +++ b/box_sdk_gen/managers/webhooks.py @@ -0,0 +1,497 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from typing import List + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.webhooks import Webhooks + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.webhook import Webhook + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import compute_webhook_signature + +from box_sdk_gen.internal.utils import compare_signatures + +from box_sdk_gen.internal.utils import date_time_from_string + +from box_sdk_gen.internal.utils import get_epoch_time_in_seconds + +from box_sdk_gen.internal.utils import date_time_to_epoch_seconds + + +class CreateWebhookTargetTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class CreateWebhookTarget(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CreateWebhookTargetTypeField] = None, + **kwargs + ): + """ + :param id: The ID of the item to trigger a webhook., defaults to None + :type id: Optional[str], optional + :param type: The type of item to trigger a webhook., defaults to None + :type type: Optional[CreateWebhookTargetTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CreateWebhookTriggers(str, Enum): + FILE_UPLOADED = 'FILE.UPLOADED' + FILE_PREVIEWED = 'FILE.PREVIEWED' + FILE_DOWNLOADED = 'FILE.DOWNLOADED' + FILE_TRASHED = 'FILE.TRASHED' + FILE_DELETED = 'FILE.DELETED' + FILE_RESTORED = 'FILE.RESTORED' + FILE_COPIED = 'FILE.COPIED' + FILE_MOVED = 'FILE.MOVED' + FILE_LOCKED = 'FILE.LOCKED' + FILE_UNLOCKED = 'FILE.UNLOCKED' + FILE_RENAMED = 'FILE.RENAMED' + COMMENT_CREATED = 'COMMENT.CREATED' + COMMENT_UPDATED = 'COMMENT.UPDATED' + COMMENT_DELETED = 'COMMENT.DELETED' + TASK_ASSIGNMENT_CREATED = 'TASK_ASSIGNMENT.CREATED' + TASK_ASSIGNMENT_UPDATED = 'TASK_ASSIGNMENT.UPDATED' + METADATA_INSTANCE_CREATED = 'METADATA_INSTANCE.CREATED' + METADATA_INSTANCE_UPDATED = 'METADATA_INSTANCE.UPDATED' + METADATA_INSTANCE_DELETED = 'METADATA_INSTANCE.DELETED' + FOLDER_CREATED = 'FOLDER.CREATED' + FOLDER_RENAMED = 'FOLDER.RENAMED' + FOLDER_DOWNLOADED = 'FOLDER.DOWNLOADED' + FOLDER_RESTORED = 'FOLDER.RESTORED' + FOLDER_DELETED = 'FOLDER.DELETED' + FOLDER_COPIED = 'FOLDER.COPIED' + FOLDER_MOVED = 'FOLDER.MOVED' + FOLDER_TRASHED = 'FOLDER.TRASHED' + WEBHOOK_DELETED = 'WEBHOOK.DELETED' + COLLABORATION_CREATED = 'COLLABORATION.CREATED' + COLLABORATION_ACCEPTED = 'COLLABORATION.ACCEPTED' + COLLABORATION_REJECTED = 'COLLABORATION.REJECTED' + COLLABORATION_REMOVED = 'COLLABORATION.REMOVED' + COLLABORATION_UPDATED = 'COLLABORATION.UPDATED' + SHARED_LINK_DELETED = 'SHARED_LINK.DELETED' + SHARED_LINK_CREATED = 'SHARED_LINK.CREATED' + SHARED_LINK_UPDATED = 'SHARED_LINK.UPDATED' + SIGN_REQUEST_COMPLETED = 'SIGN_REQUEST.COMPLETED' + SIGN_REQUEST_DECLINED = 'SIGN_REQUEST.DECLINED' + SIGN_REQUEST_EXPIRED = 'SIGN_REQUEST.EXPIRED' + SIGN_REQUEST_SIGNER_EMAIL_BOUNCED = 'SIGN_REQUEST.SIGNER_EMAIL_BOUNCED' + SIGN_REQUEST_SIGN_SIGNER_SIGNED = 'SIGN_REQUEST.SIGN_SIGNER_SIGNED' + SIGN_REQUEST_SIGN_DOCUMENT_CREATED = 'SIGN_REQUEST.SIGN_DOCUMENT_CREATED' + SIGN_REQUEST_SIGN_ERROR_FINALIZING = 'SIGN_REQUEST.SIGN_ERROR_FINALIZING' + + +class UpdateWebhookByIdTargetTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class UpdateWebhookByIdTarget(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[UpdateWebhookByIdTargetTypeField] = None, + **kwargs + ): + """ + :param id: The ID of the item to trigger a webhook., defaults to None + :type id: Optional[str], optional + :param type: The type of item to trigger a webhook., defaults to None + :type type: Optional[UpdateWebhookByIdTargetTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class UpdateWebhookByIdTriggers(str, Enum): + FILE_UPLOADED = 'FILE.UPLOADED' + FILE_PREVIEWED = 'FILE.PREVIEWED' + FILE_DOWNLOADED = 'FILE.DOWNLOADED' + FILE_TRASHED = 'FILE.TRASHED' + FILE_DELETED = 'FILE.DELETED' + FILE_RESTORED = 'FILE.RESTORED' + FILE_COPIED = 'FILE.COPIED' + FILE_MOVED = 'FILE.MOVED' + FILE_LOCKED = 'FILE.LOCKED' + FILE_UNLOCKED = 'FILE.UNLOCKED' + FILE_RENAMED = 'FILE.RENAMED' + COMMENT_CREATED = 'COMMENT.CREATED' + COMMENT_UPDATED = 'COMMENT.UPDATED' + COMMENT_DELETED = 'COMMENT.DELETED' + TASK_ASSIGNMENT_CREATED = 'TASK_ASSIGNMENT.CREATED' + TASK_ASSIGNMENT_UPDATED = 'TASK_ASSIGNMENT.UPDATED' + METADATA_INSTANCE_CREATED = 'METADATA_INSTANCE.CREATED' + METADATA_INSTANCE_UPDATED = 'METADATA_INSTANCE.UPDATED' + METADATA_INSTANCE_DELETED = 'METADATA_INSTANCE.DELETED' + FOLDER_CREATED = 'FOLDER.CREATED' + FOLDER_RENAMED = 'FOLDER.RENAMED' + FOLDER_DOWNLOADED = 'FOLDER.DOWNLOADED' + FOLDER_RESTORED = 'FOLDER.RESTORED' + FOLDER_DELETED = 'FOLDER.DELETED' + FOLDER_COPIED = 'FOLDER.COPIED' + FOLDER_MOVED = 'FOLDER.MOVED' + FOLDER_TRASHED = 'FOLDER.TRASHED' + WEBHOOK_DELETED = 'WEBHOOK.DELETED' + COLLABORATION_CREATED = 'COLLABORATION.CREATED' + COLLABORATION_ACCEPTED = 'COLLABORATION.ACCEPTED' + COLLABORATION_REJECTED = 'COLLABORATION.REJECTED' + COLLABORATION_REMOVED = 'COLLABORATION.REMOVED' + COLLABORATION_UPDATED = 'COLLABORATION.UPDATED' + SHARED_LINK_DELETED = 'SHARED_LINK.DELETED' + SHARED_LINK_CREATED = 'SHARED_LINK.CREATED' + SHARED_LINK_UPDATED = 'SHARED_LINK.UPDATED' + SIGN_REQUEST_COMPLETED = 'SIGN_REQUEST.COMPLETED' + SIGN_REQUEST_DECLINED = 'SIGN_REQUEST.DECLINED' + SIGN_REQUEST_EXPIRED = 'SIGN_REQUEST.EXPIRED' + SIGN_REQUEST_SIGNER_EMAIL_BOUNCED = 'SIGN_REQUEST.SIGNER_EMAIL_BOUNCED' + SIGN_REQUEST_SIGN_SIGNER_SIGNED = 'SIGN_REQUEST.SIGN_SIGNER_SIGNED' + SIGN_REQUEST_SIGN_DOCUMENT_CREATED = 'SIGN_REQUEST.SIGN_DOCUMENT_CREATED' + SIGN_REQUEST_SIGN_ERROR_FINALIZING = 'SIGN_REQUEST.SIGN_ERROR_FINALIZING' + + +class WebhooksManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_webhooks( + self, + *, + marker: Optional[str] = None, + limit: Optional[int] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Webhooks: + """ + Returns all defined webhooks for the requesting application. + + This API only returns webhooks that are applied to files or folders that are + + + owned by the authenticated user. This means that an admin can not see webhooks + + + created by a service account unless the admin has access to those folders, and + + + vice versa. + + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + {'marker': to_string(marker), 'limit': to_string(limit)} + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/webhooks']), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Webhooks) + + def create_webhook( + self, + target: CreateWebhookTarget, + address: str, + triggers: List[CreateWebhookTriggers], + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Webhook: + """ + Creates a webhook. + :param target: The item that will trigger the webhook. + :type target: CreateWebhookTarget + :param address: The URL that is notified by this webhook. + :type address: str + :param triggers: An array of event names that this webhook is + to be triggered for. + :type triggers: List[CreateWebhookTriggers] + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'target': target, + 'address': address, + 'triggers': triggers, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join([self.network_session.base_urls.base_url, '/2.0/webhooks']), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Webhook) + + def get_webhook_by_id( + self, + webhook_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Webhook: + """ + Retrieves a specific webhook. + :param webhook_id: The ID of the webhook. + Example: "3321123" + :type webhook_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/webhooks/', + to_string(webhook_id), + ] + ), + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Webhook) + + def update_webhook_by_id( + self, + webhook_id: str, + *, + target: Optional[UpdateWebhookByIdTarget] = None, + address: Optional[str] = None, + triggers: Optional[List[UpdateWebhookByIdTriggers]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Webhook: + """ + Updates a webhook. + :param webhook_id: The ID of the webhook. + Example: "3321123" + :type webhook_id: str + :param target: The item that will trigger the webhook., defaults to None + :type target: Optional[UpdateWebhookByIdTarget], optional + :param address: The URL that is notified by this webhook., defaults to None + :type address: Optional[str], optional + :param triggers: An array of event names that this webhook is + to be triggered for., defaults to None + :type triggers: Optional[List[UpdateWebhookByIdTriggers]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'target': target, + 'address': address, + 'triggers': triggers, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/webhooks/', + to_string(webhook_id), + ] + ), + method='PUT', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Webhook) + + def delete_webhook_by_id( + self, + webhook_id: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Deletes a webhook. + :param webhook_id: The ID of the webhook. + Example: "3321123" + :type webhook_id: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/webhooks/', + to_string(webhook_id), + ] + ), + method='DELETE', + headers=headers_map, + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None + + @staticmethod + def validate_message( + body: str, + headers: Dict[str, str], + primary_key: str, + *, + secondary_key: Optional[str] = None, + max_age: Optional[int] = 600 + ) -> bool: + """ + Validate a webhook message by verifying the signature and the delivery timestamp + :param body: The request body of the webhook message + :type body: str + :param headers: The headers of the webhook message + :type headers: Dict[str, str] + :param primary_key: The primary signature to verify the message with + :type primary_key: str + :param secondary_key: The secondary signature to verify the message with, defaults to None + :type secondary_key: Optional[str], optional + :param max_age: The maximum age of the message in seconds, defaults to 10 minutes, defaults to 600 + :type max_age: Optional[int], optional + """ + delivery_timestamp: DateTime = date_time_from_string( + headers.get('box-delivery-timestamp') + ) + current_epoch: int = get_epoch_time_in_seconds() + if ( + current_epoch - max_age > date_time_to_epoch_seconds(delivery_timestamp) + or date_time_to_epoch_seconds(delivery_timestamp) > current_epoch + ): + return False + if ( + not primary_key == None and not headers.get('box-signature-primary') == None + ) and compare_signatures( + expected_signature=compute_webhook_signature( + body, headers, primary_key, escape_body=False + ), + received_signature=headers.get('box-signature-primary'), + ): + return True + if ( + not primary_key == None and not headers.get('box-signature-primary') == None + ) and compare_signatures( + expected_signature=compute_webhook_signature( + body, headers, primary_key, escape_body=True + ), + received_signature=headers.get('box-signature-primary'), + ): + return True + if ( + not secondary_key == None + and not headers.get('box-signature-secondary') == None + ) and compare_signatures( + expected_signature=compute_webhook_signature( + body, headers, secondary_key, escape_body=False + ), + received_signature=headers.get('box-signature-secondary'), + ): + return True + if ( + not secondary_key == None + and not headers.get('box-signature-secondary') == None + ) and compare_signatures( + expected_signature=compute_webhook_signature( + body, headers, secondary_key, escape_body=True + ), + received_signature=headers.get('box-signature-secondary'), + ): + return True + return False diff --git a/box_sdk_gen/managers/workflows.py b/box_sdk_gen/managers/workflows.py new file mode 100644 index 000000000..7e0881040 --- /dev/null +++ b/box_sdk_gen/managers/workflows.py @@ -0,0 +1,261 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.serialization.json import deserialize + +from typing import List + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.workflows import Workflows + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.outcome import Outcome + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import sd_to_json + +from box_sdk_gen.serialization.json import SerializedData + + +class StartWorkflowType(str, Enum): + WORKFLOW_PARAMETERS = 'workflow_parameters' + + +class StartWorkflowFlow(BaseObject): + def __init__( + self, *, type: Optional[str] = None, id: Optional[str] = None, **kwargs + ): + """ + :param type: The type of the flow object., defaults to None + :type type: Optional[str], optional + :param id: The id of the flow., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class StartWorkflowFilesTypeField(str, Enum): + FILE = 'file' + + +class StartWorkflowFiles(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + *, + type: Optional[StartWorkflowFilesTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of the file object., defaults to None + :type type: Optional[StartWorkflowFilesTypeField], optional + :param id: The id of the file., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class StartWorkflowFolderTypeField(str, Enum): + FOLDER = 'folder' + + +class StartWorkflowFolder(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + *, + type: Optional[StartWorkflowFolderTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of the folder object., defaults to None + :type type: Optional[StartWorkflowFolderTypeField], optional + :param id: The id of the folder., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class WorkflowsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def get_workflows( + self, + folder_id: str, + *, + trigger_type: Optional[str] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> Workflows: + """ + Returns list of workflows that act on a given `folder ID`, and + + have a flow with a trigger type of `WORKFLOW_MANUAL_START`. + + + You application must be authorized to use the `Manage Box Relay` application + + + scope within the developer console in to use this endpoint. + + :param folder_id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting this folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folder/123` + the `folder_id` is `123`. + + The root folder of a Box account is + always represented by the ID `0`. + :type folder_id: str + :param trigger_type: Type of trigger to search for., defaults to None + :type trigger_type: Optional[str], optional + :param limit: The maximum number of items to return per page., defaults to None + :type limit: Optional[int], optional + :param marker: Defines the position marker at which to begin returning results. This is + used when paginating using marker-based pagination. + + This requires `usemarker` to be set to `true`., defaults to None + :type marker: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + query_params_map: Dict[str, str] = prepare_params( + { + 'folder_id': to_string(folder_id), + 'trigger_type': to_string(trigger_type), + 'limit': to_string(limit), + 'marker': to_string(marker), + } + ) + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/workflows'] + ), + method='GET', + params=query_params_map, + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, Workflows) + + def start_workflow( + self, + workflow_id: str, + flow: StartWorkflowFlow, + files: List[StartWorkflowFiles], + folder: StartWorkflowFolder, + *, + type: Optional[StartWorkflowType] = None, + outcomes: Optional[List[Outcome]] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> None: + """ + Initiates a flow with a trigger type of `WORKFLOW_MANUAL_START`. + + You application must be authorized to use the `Manage Box Relay` application + + + scope within the developer console. + + :param workflow_id: The ID of the workflow. + Example: "12345" + :type workflow_id: str + :param flow: The flow that will be triggered. + :type flow: StartWorkflowFlow + :param files: The array of files for which the workflow should start. All files + must be in the workflow's configured folder. + :type files: List[StartWorkflowFiles] + :param folder: The folder object for which the workflow is configured. + :type folder: StartWorkflowFolder + :param type: The type of the parameters object., defaults to None + :type type: Optional[StartWorkflowType], optional + :param outcomes: A configurable outcome the workflow should complete., defaults to None + :type outcomes: Optional[List[Outcome]], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = { + 'type': type, + 'flow': flow, + 'files': files, + 'folder': folder, + 'outcomes': outcomes, + } + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [ + self.network_session.base_urls.base_url, + '/2.0/workflows/', + to_string(workflow_id), + '/start', + ] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.NO_CONTENT, + auth=self.auth, + network_session=self.network_session, + ) + ) + return None diff --git a/box_sdk_gen/managers/zip_downloads.py b/box_sdk_gen/managers/zip_downloads.py new file mode 100644 index 000000000..7813cbdc2 --- /dev/null +++ b/box_sdk_gen/managers/zip_downloads.py @@ -0,0 +1,318 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.serialization.json import serialize + +from box_sdk_gen.serialization.json import deserialize + +from box_sdk_gen.networking.fetch_options import ResponseFormat + +from box_sdk_gen.schemas.zip_download import ZipDownload + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.zip_download_request import ZipDownloadRequest + +from box_sdk_gen.schemas.zip_download_status import ZipDownloadStatus + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.networking.auth import Authentication + +from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import prepare_params + +from box_sdk_gen.internal.utils import to_string + +from box_sdk_gen.internal.utils import ByteStream + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.serialization.json import sd_to_json + + +class CreateZipDownloadItemsTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class CreateZipDownloadItems(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__(self, type: CreateZipDownloadItemsTypeField, id: str, **kwargs): + """ + :param type: The type of the item to add to the archive. + :type type: CreateZipDownloadItemsTypeField + :param id: The identifier of the item to add to the archive. When this item is + a folder then this can not be the root folder with ID `0`. + :type id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class DownloadZipItemsTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class DownloadZipItems(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__(self, type: DownloadZipItemsTypeField, id: str, **kwargs): + """ + :param type: The type of the item to add to the archive. + :type type: DownloadZipItemsTypeField + :param id: The identifier of the item to add to the archive. When this item is + a folder then this can not be the root folder with ID `0`. + :type id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class ZipDownloadsManager: + def __init__( + self, + *, + auth: Optional[Authentication] = None, + network_session: NetworkSession = None + ): + if network_session is None: + network_session = NetworkSession() + self.auth = auth + self.network_session = network_session + + def create_zip_download( + self, + items: List[CreateZipDownloadItems], + *, + download_file_name: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ZipDownload: + """ + Creates a request to download multiple files and folders as a single `zip` + + archive file. This API does not return the archive but instead performs all + + + the checks to ensure that the user has access to all the items, and then + + + returns a `download_url` and a `status_url` that can be used to download the + + + archive. + + + The limit for an archive is either the Account's upload limit or + + + 10,000 files, whichever is met first. + + + **Note**: Downloading a large file can be + + + affected by various + + + factors such as distance, network latency, + + + bandwidth, and congestion, as well as packet loss + + + ratio and current server load. + + + For these reasons we recommend that a maximum ZIP archive + + + total size does not exceed 25GB. + + :param items: A list of items to add to the `zip` archive. These can + be folders or files. + :type items: List[CreateZipDownloadItems] + :param download_file_name: The optional name of the `zip` archive. This name will be appended by the + `.zip` file extension, for example `January Financials.zip`., defaults to None + :type download_file_name: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'items': items, 'download_file_name': download_file_name} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=''.join( + [self.network_session.base_urls.base_url, '/2.0/zip_downloads'] + ), + method='POST', + headers=headers_map, + data=serialize(request_body), + content_type='application/json', + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ZipDownload) + + def get_zip_download_content( + self, + download_url: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ByteStream: + """ + Returns the contents of a `zip` archive in binary format. This URL does not + + require any form of authentication and could be used in a user's browser to + + + download the archive to a user's device. + + + By default, this URL is only valid for a few seconds from the creation of + + + the request for this archive. Once a download has started it can not be + + + stopped and resumed, instead a new request for a zip archive would need to + + + be created. + + + The URL of this endpoint should not be considered as fixed. Instead, use + + + the [Create zip download](e://post_zip_downloads) API to request to create a + + + `zip` archive, and then follow the `download_url` field in the response to + + + this endpoint. + + :param download_url: The URL that can be used to download created `zip` archive. + Example: `https://dl.boxcloud.com/2.0/zip_downloads/29l00nfxDyHOt7RphI9zT_w==nDnZEDjY2S8iEWWCHEEiptFxwoWojjlibZjJ6geuE5xnXENDTPxzgbks_yY=/content` + :type download_url: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=download_url, + method='GET', + headers=headers_map, + response_format=ResponseFormat.BINARY, + auth=self.auth, + network_session=self.network_session, + ) + ) + return response.content + + def get_zip_download_status( + self, + status_url: str, + *, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ZipDownloadStatus: + """ + Returns the download status of a `zip` archive, allowing an application to + + inspect the progress of the download as well as the number of items that + + + might have been skipped. + + + This endpoint can only be accessed once the download has started. + + + Subsequently this endpoint is valid for 12 hours from the start of the + + + download. + + + The URL of this endpoint should not be considered as fixed. Instead, use + + + the [Create zip download](e://post_zip_downloads) API to request to create a + + + `zip` archive, and then follow the `status_url` field in the response to + + + this endpoint. + + :param status_url: The URL that can be used to get the status of the `zip` archive being downloaded. + Example: `https://dl.boxcloud.com/2.0/zip_downloads/29l00nfxDyHOt7RphI9zT_w==nDnZEDjY2S8iEWWCHEEiptFxwoWojjlibZjJ6geuE5xnXENDTPxzgbks_yY=/status` + :type status_url: str + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + headers_map: Dict[str, str] = prepare_params({**extra_headers}) + response: FetchResponse = self.network_session.network_client.fetch( + FetchOptions( + url=status_url, + method='GET', + headers=headers_map, + response_format=ResponseFormat.JSON, + auth=self.auth, + network_session=self.network_session, + ) + ) + return deserialize(response.data, ZipDownloadStatus) + + def download_zip( + self, + items: List[DownloadZipItems], + *, + download_file_name: Optional[str] = None, + extra_headers: Optional[Dict[str, Optional[str]]] = None + ) -> ByteStream: + """ + Creates a zip and downloads its content + :param items: A list of items to add to the `zip` archive. These can + be folders or files. + :type items: List[DownloadZipItems] + :param download_file_name: The optional name of the `zip` archive. This name will be appended by the + `.zip` file extension, for example `January Financials.zip`., defaults to None + :type download_file_name: Optional[str], optional + :param extra_headers: Extra headers that will be included in the HTTP request., defaults to None + :type extra_headers: Optional[Dict[str, Optional[str]]], optional + """ + if extra_headers is None: + extra_headers = {} + request_body: Dict = {'items': items, 'download_file_name': download_file_name} + zip_download_session: ZipDownload = self.create_zip_download( + items, download_file_name=download_file_name, extra_headers=extra_headers + ) + return self.get_zip_download_content( + zip_download_session.download_url, extra_headers=extra_headers + ) diff --git a/box_sdk_gen/networking/__init__.py b/box_sdk_gen/networking/__init__.py new file mode 100644 index 000000000..c12260c17 --- /dev/null +++ b/box_sdk_gen/networking/__init__.py @@ -0,0 +1,19 @@ +from box_sdk_gen.networking.box_network_client import * + +from box_sdk_gen.networking.proxy_config import * + +from box_sdk_gen.networking.network import * + +from box_sdk_gen.networking.auth import * + +from box_sdk_gen.networking.fetch_options import * + +from box_sdk_gen.networking.fetch_response import * + +from box_sdk_gen.networking.network_client import * + +from box_sdk_gen.networking.retries import * + +from box_sdk_gen.networking.base_urls import * + +from box_sdk_gen.networking.version import * diff --git a/box_sdk_gen/networking/auth.py b/box_sdk_gen/networking/auth.py new file mode 100644 index 000000000..61347ff3c --- /dev/null +++ b/box_sdk_gen/networking/auth.py @@ -0,0 +1,47 @@ +from typing import Optional + +from abc import abstractmethod + +from typing import List + +from box_sdk_gen.schemas.access_token import AccessToken + +from box_sdk_gen.networking.network import NetworkSession + + +class Authentication: + def __init__(self): + pass + + @abstractmethod + def retrieve_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + pass + + @abstractmethod + def refresh_token( + self, *, network_session: Optional[NetworkSession] = None + ) -> AccessToken: + pass + + @abstractmethod + def retrieve_authorization_header( + self, *, network_session: Optional[NetworkSession] = None + ) -> str: + pass + + @abstractmethod + def revoke_token(self, *, network_session: Optional[NetworkSession] = None) -> None: + pass + + @abstractmethod + def downscope_token( + self, + scopes: List[str], + *, + resource: Optional[str] = None, + shared_link: Optional[str] = None, + network_session: Optional[NetworkSession] = None + ) -> AccessToken: + pass diff --git a/box_sdk_gen/networking/base_urls.py b/box_sdk_gen/networking/base_urls.py new file mode 100644 index 000000000..6b59444f7 --- /dev/null +++ b/box_sdk_gen/networking/base_urls.py @@ -0,0 +1,29 @@ +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class BaseUrls(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'oauth_2_url': 'oauth2_url', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'oauth2_url': 'oauth_2_url', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + base_url: str = 'https://api.box.com', + upload_url: str = 'https://upload.box.com/api', + oauth_2_url: str = 'https://account.box.com/api/oauth2', + **kwargs + ): + super().__init__(**kwargs) + self.base_url = base_url + self.upload_url = upload_url + self.oauth_2_url = oauth_2_url diff --git a/box_sdk_gen/networking/box_network_client.py b/box_sdk_gen/networking/box_network_client.py new file mode 100644 index 000000000..7a4f44647 --- /dev/null +++ b/box_sdk_gen/networking/box_network_client.py @@ -0,0 +1,363 @@ +import io + +import time +from collections import OrderedDict +from dataclasses import dataclass +from typing import Optional, Dict, Union +from sys import version_info as py_version + +import requests +from requests import RequestException, Session, Response +from requests_toolbelt import MultipartEncoder + +from ..internal.logging import DataSanitizer +from .retries import BoxRetryStrategy +from ..networking.fetch_options import FetchOptions +from ..networking.fetch_response import FetchResponse +from ..box.errors import BoxAPIError, BoxSDKError, RequestInfo, ResponseInfo +from ..internal.utils import ByteStream, ResponseByteStream +from ..networking.network_client import NetworkClient +from ..serialization.json import ( + sd_to_json, + sd_to_url_params, + json_to_serialized_data, +) +from ..networking.version import __version__ + +SDK_VERSION = __version__ +USER_AGENT_HEADER = f'box-python-generated-sdk-{SDK_VERSION}' +X_BOX_UA_HEADER = ( + f'agent=box-python-generated-sdk/{SDK_VERSION}; ' + f'env=python/{py_version.major}.{py_version.minor}.{py_version.micro}' +) + + +@dataclass +class APIRequest: + method: str + url: str + headers: Dict[str, str] + params: Dict[str, str] + data: Optional[Union[str, ByteStream, MultipartEncoder]] + allow_redirects: bool = True + + +@dataclass +class APIResponse: + network_response: Optional[Response] = None + reauthentication_needed: Optional[bool] = False + raised_exception: Optional[Exception] = None + + def get_header( + self, header_name: str, default_value: Optional[str] = None + ) -> Optional[str]: + try: + return self.network_response.headers[header_name] + except (ValueError, KeyError, AttributeError): + return default_value + + +class BoxNetworkClient(NetworkClient): + def __init__(self, requests_session: Optional[Session] = None): + super().__init__() + self.requests_session = requests_session or requests.Session() + + def fetch(self, options: 'FetchOptions') -> FetchResponse: + retry_strategy = ( + options.network_session.retry_strategy + if options.network_session + else BoxRetryStrategy() + ) + data_sanitizer = ( + options.network_session.data_sanitizer + if options.network_session + else DataSanitizer() + ) + + attempt_nr = 1 + number_of_retries_on_exception = 0 + response = APIResponse() + + options_stream_position = self._get_options_stream_position(options) + multipart_streams_positions = self._get_multipart_stream_positions(options) + + while True: + request: APIRequest = self._prepare_request( + options=options, reauthenticate=response.reauthentication_needed + ) + response: APIResponse = self._make_request(request=request) + if response.network_response is not None: + attempt_for_retry = attempt_nr + network_response = response.network_response + + if options.response_format == 'binary': + fetch_response = FetchResponse( + url=network_response.url, + status=network_response.status_code, + headers=dict(response.network_response.headers), + content=ResponseByteStream( + response.network_response.iter_content(chunk_size=1024) + ), + ) + else: + fetch_response = FetchResponse( + url=network_response.url, + status=network_response.status_code, + headers=dict(response.network_response.headers), + data=(self._read_json_body(network_response.text)), + content=io.BytesIO(network_response.content), + ) + else: + number_of_retries_on_exception += 1 + attempt_for_retry = number_of_retries_on_exception + fetch_response = FetchResponse(status=0, headers={}) + + attempt_nr += 1 + should_retry = retry_strategy.should_retry( + fetch_options=options, + fetch_response=fetch_response, + attempt_number=attempt_for_retry, + ) + + if should_retry: + self._reset_options_stream( + options, options_stream_position, response.raised_exception + ) + self._reset_multipart_streams( + options, multipart_streams_positions, response.raised_exception + ) + time.sleep( + retry_strategy.retry_after( + fetch_options=options, + fetch_response=fetch_response, + attempt_number=attempt_for_retry, + ) + ) + continue + + if 200 <= fetch_response.status < 400: + return fetch_response + break + self._raise_on_unsuccessful_request( + request=request, response=response, data_sanitizer=data_sanitizer + ) + + def _prepare_request( + self, options: 'FetchOptions', reauthenticate: bool = False + ) -> APIRequest: + headers = self._prepare_headers(options, reauthenticate) + params = options.params or {} + data = self._prepare_body( + options.content_type, options.file_stream or options.data + ) + allow_redirects = options.follow_redirects + + if options.content_type: + if options.content_type == 'multipart/form-data': + fields = OrderedDict() + for part in options.multipart_data: + if part.data: + fields[part.part_name] = sd_to_json(part.data) + else: + fields[part.part_name] = ( + part.file_name or '', + part.file_stream, + part.content_type, + ) + + multipart_stream = MultipartEncoder(fields) + data = multipart_stream + headers['Content-Type'] = multipart_stream.content_type + else: + headers['Content-Type'] = options.content_type + + return APIRequest( + method=options.method, + url=options.url, + headers=headers, + params=params, + data=data, + allow_redirects=allow_redirects, + ) + + @staticmethod + def _prepare_headers( + options: 'FetchOptions', reauthenticate: bool = False + ) -> Dict[str, str]: + headers = {} + if options.network_session: + headers.update(options.network_session.additional_headers) + if options.headers: + headers.update(options.headers) + if options.auth: + if reauthenticate: + options.auth.refresh_token(network_session=options.network_session) + headers['Authorization'] = options.auth.retrieve_authorization_header( + network_session=options.network_session + ) + + headers['User-Agent'] = USER_AGENT_HEADER + headers['X-Box-UA'] = X_BOX_UA_HEADER + return headers + + @staticmethod + def _prepare_body( + content_type: str, data: Union[dict, ByteStream] + ) -> Optional[Union[str, ByteStream]]: + if ( + content_type == 'application/json' + or content_type == 'application/json-patch+json' + ): + return sd_to_json(data) if data else None + if content_type == 'application/x-www-form-urlencoded': + return sd_to_url_params(data) + if ( + content_type == 'multipart/form-data' + or content_type == 'application/octet-stream' + ): + return data + raise + + def _make_request(self, request: APIRequest) -> APIResponse: + raised_exception = None + reauthentication_needed = False + default_timeout = (5, 60) # connect, read timeout + try: + network_response = self.requests_session.request( + method=request.method, + url=request.url, + headers=request.headers, + data=request.data, + params=request.params, + allow_redirects=request.allow_redirects, + stream=True, + timeout=default_timeout, + ) + except RequestException as request_exc: + raised_exception = request_exc + network_response = None + + if 'EOF occurred in violation of protocol' in str(request_exc): + reauthentication_needed = True + + return APIResponse( + network_response=network_response, + reauthentication_needed=reauthentication_needed, + raised_exception=raised_exception, + ) + + @staticmethod + def _raise_on_unsuccessful_request( + request: APIRequest, response: APIResponse, data_sanitizer: DataSanitizer + ) -> None: + if response.raised_exception: + raise BoxSDKError( + message=str(response.raised_exception), error=response.raised_exception + ) + + network_response = response.network_response + response_json = BoxNetworkClient._read_json_body(network_response.text) + + raise BoxAPIError( + message=f'{network_response.status_code} {response_json.get("message", "")}; Request ID: {response_json.get("request_id", "")}', + request_info=RequestInfo( + method=request.method, + url=request.url, + query_params=request.params, + headers=request.headers, + body=request.data, + ), + response_info=ResponseInfo( + status_code=network_response.status_code, + headers=dict(network_response.headers), + body=response_json, + raw_body=network_response.text, + code=response_json.get("code", None), + context_info=response_json.get("context_info", {}), + request_id=response_json.get("request_id", None), + help_url=response_json.get("help_url", None), + ), + data_sanitizer=data_sanitizer, + ) + + @staticmethod + def _get_multipart_stream_positions(options: 'FetchOptions') -> dict: + multipart_streams_positions = {} + if options.multipart_data: + for part in options.multipart_data: + if part.file_stream and part.file_stream.seekable(): + multipart_streams_positions[part.part_name] = ( + part.file_stream.tell() + ) + return multipart_streams_positions + + @staticmethod + def _get_options_stream_position(options: 'FetchOptions') -> int: + filestream_position = 0 + if options.file_stream and options.file_stream.seekable(): + filestream_position = options.file_stream.tell() + return filestream_position + + @staticmethod + def _validate_seekable(stream: ByteStream, raised_exception: Optional[Exception]): + if not stream.seekable(): + raise BoxSDKError( + message='Request with non-seekable stream cannot be retried', + error=raised_exception, + ) + + @staticmethod + def _read_json_body(response_body: str) -> dict: + if not response_body: + return {} + try: + return json_to_serialized_data(response_body) + except (ValueError, TypeError): + return {} + + def _reset_stream( + self, + stream: ByteStream, + original_position: int, + raised_exception: Optional[Exception], + ): + self._validate_seekable(stream, raised_exception) + stream.seek(original_position) + + def _reset_options_stream( + self, + options: 'FetchOptions', + filestream_position: int, + raised_exception: Optional[Exception], + ): + if options.file_stream: + self._reset_stream( + options.file_stream, filestream_position, raised_exception + ) + + def _reset_multipart_streams( + self, + options: 'FetchOptions', + multipart_streams_positions: dict, + raised_exception: Optional[Exception], + ): + if not options.multipart_data: + return + + for part in options.multipart_data: + if not part.file_stream: + continue + + position = multipart_streams_positions.get(part.part_name) + # we didn't get position before sending request hence the stream must be non-seekable + if position is None: + raise BoxSDKError( + message='Request with non-seekable stream cannot be retried', + error=raised_exception, + ) + + self._reset_stream( + part.file_stream, + multipart_streams_positions[part.part_name], + raised_exception, + ) diff --git a/box_sdk_gen/networking/fetch_options.py b/box_sdk_gen/networking/fetch_options.py new file mode 100644 index 000000000..84d85f215 --- /dev/null +++ b/box_sdk_gen/networking/fetch_options.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from enum import Enum + +from typing import Optional + +from typing import Dict + +from typing import List + +if TYPE_CHECKING: + from box_sdk_gen.networking.auth import Authentication + +if TYPE_CHECKING: + from box_sdk_gen.networking.network import NetworkSession + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import ByteStream + + +class ResponseFormat(str, Enum): + JSON = 'json' + BINARY = 'binary' + NO_CONTENT = 'no_content' + + +class MultipartItem: + def __init__( + self, + part_name: str, + *, + data: Optional[SerializedData] = None, + file_stream: Optional[ByteStream] = None, + file_name: Optional[str] = None, + content_type: Optional[str] = None, + ): + """ + :param part_name: Name of the part + :type part_name: str + :param data: Data of the part, defaults to None + :type data: Optional[SerializedData], optional + :param file_stream: File stream of the part, defaults to None + :type file_stream: Optional[ByteStream], optional + :param file_name: File name of the part, defaults to None + :type file_name: Optional[str], optional + :param content_type: Content type of the part, defaults to None + :type content_type: Optional[str], optional + """ + self.part_name = part_name + self.data = data + self.file_stream = file_stream + self.file_name = file_name + self.content_type = content_type + + +class FetchOptions: + def __init__( + self, + url: str, + method: str, + *, + params: Optional[Dict[str, str]] = None, + headers: Optional[Dict[str, str]] = None, + data: Optional[SerializedData] = None, + file_stream: Optional[ByteStream] = None, + multipart_data: Optional[List[MultipartItem]] = None, + content_type: str = 'application/json', + response_format: ResponseFormat = ResponseFormat.JSON, + auth: Optional[Authentication] = None, + network_session: Optional[NetworkSession] = None, + follow_redirects: Optional[bool] = True, + ): + """ + :param url: URL of the request + :type url: str + :param method: HTTP verb of the request + :type method: str + :param params: HTTP query parameters, defaults to None + :type params: Optional[Dict[str, str]], optional + :param headers: HTTP headers, defaults to None + :type headers: Optional[Dict[str, str]], optional + :param data: Request body of the request, defaults to None + :type data: Optional[SerializedData], optional + :param file_stream: Stream data of the request, defaults to None + :type file_stream: Optional[ByteStream], optional + :param multipart_data: Multipart data of the request, defaults to None + :type multipart_data: Optional[List[MultipartItem]], optional + :param content_type: Content type of the request body, defaults to 'application/json' + :type content_type: str, optional + :param response_format: Expected response format, defaults to ResponseFormat.JSON + :type response_format: ResponseFormat, optional + :param auth: Authentication object, defaults to None + :type auth: Optional[Authentication], optional + :param network_session: Network session object, defaults to None + :type network_session: Optional[NetworkSession], optional + :param follow_redirects: A boolean value indicate if the request should follow redirects. Defaults to True. Not supported in Browser environment., defaults to True + :type follow_redirects: Optional[bool], optional + """ + self.url = url + self.method = method + self.params = params + self.headers = headers + self.data = data + self.file_stream = file_stream + self.multipart_data = multipart_data + self.content_type = content_type + self.response_format = response_format + self.auth = auth + self.network_session = network_session + self.follow_redirects = follow_redirects diff --git a/box_sdk_gen/networking/fetch_response.py b/box_sdk_gen/networking/fetch_response.py new file mode 100644 index 000000000..48675119b --- /dev/null +++ b/box_sdk_gen/networking/fetch_response.py @@ -0,0 +1,36 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.serialization.json import SerializedData + +from box_sdk_gen.internal.utils import ByteStream + + +class FetchResponse: + def __init__( + self, + status: int, + headers: Dict[str, str], + *, + url: Optional[str] = None, + data: Optional[SerializedData] = None, + content: Optional[ByteStream] = None + ): + """ + :param status: HTTP status code of the response + :type status: int + :param headers: HTTP headers of the response + :type headers: Dict[str, str] + :param url: URL of the response, defaults to None + :type url: Optional[str], optional + :param data: Response body of the response, defaults to None + :type data: Optional[SerializedData], optional + :param content: Streamed content of the response, defaults to None + :type content: Optional[ByteStream], optional + """ + self.status = status + self.headers = headers + self.url = url + self.data = data + self.content = content diff --git a/box_sdk_gen/networking/network.py b/box_sdk_gen/networking/network.py new file mode 100644 index 000000000..c51f8722e --- /dev/null +++ b/box_sdk_gen/networking/network.py @@ -0,0 +1,154 @@ +from typing import Dict + +from ..internal.logging import DataSanitizer +from .network_client import NetworkClient +from .box_network_client import BoxNetworkClient +from .proxy_config import ProxyConfig +from .base_urls import BaseUrls +from .retries import RetryStrategy, BoxRetryStrategy + + +class NetworkSession: + def __init__( + self, + *, + network_client: NetworkClient = None, + retry_strategy: RetryStrategy = None, + additional_headers: Dict[str, str] = None, + base_urls: BaseUrls = None, + proxy_url: str = None, + data_sanitizer: DataSanitizer = None, + ): + if additional_headers is None: + additional_headers = {} + if base_urls is None: + base_urls = BaseUrls() + if retry_strategy is None: + retry_strategy = BoxRetryStrategy() + if network_client is None: + network_client = BoxNetworkClient() + if ( + proxy_url + and hasattr(network_client, 'requests_session') + and network_client.requests_session + ): + network_client.requests_session.proxies = { + 'http': proxy_url, + 'https': proxy_url, + } + if data_sanitizer is None: + data_sanitizer = DataSanitizer() + self.additional_headers = additional_headers + self.base_urls = base_urls + self.proxy_url = proxy_url + self.network_client = network_client + self.retry_strategy = retry_strategy + self.data_sanitizer = data_sanitizer + + def with_additional_headers( + self, additional_headers: Dict[str, str] = None + ) -> 'NetworkSession': + """ + Generate a fresh network session by duplicating the existing configuration and network parameters, + while also including additional headers to be attached to every API call. + :param additional_headers: Dict of headers, which are appended to each API request + :return: a new instance of NetworkSession + """ + return NetworkSession( + network_client=self.network_client, + additional_headers={**self.additional_headers, **additional_headers}, + base_urls=self.base_urls, + proxy_url=self.proxy_url, + retry_strategy=self.retry_strategy, + data_sanitizer=self.data_sanitizer, + ) + + def with_custom_base_urls(self, base_urls: BaseUrls) -> 'NetworkSession': + """ + Generate a fresh network session by duplicating the existing configuration and network parameters, + while also including additional base urls to be used for each API call. + :param base_urls: Dict of base urls, which are appended to each API request + :return: a new instance of NetworkSession + """ + return NetworkSession( + network_client=self.network_client, + additional_headers=self.additional_headers, + base_urls=base_urls, + proxy_url=self.proxy_url, + retry_strategy=self.retry_strategy, + data_sanitizer=self.data_sanitizer, + ) + + def with_proxy(self, config: ProxyConfig) -> 'NetworkSession': + """ + Generate a fresh network session by duplicating the existing configuration and network parameters, + while also including a proxy to be used for each API call. + :param config: ProxyConfig object, which contains the proxy url, username, and password + :return: a new instance of NetworkSession + """ + if not config.url or not config.url.startswith("http"): + raise ValueError("Invalid proxy URL provided") + + proxy_host = config.url.split("//")[1] + proxy_auth = ( + f"{config.username}:{config.password}@" + if config.username and config.password + else "" + ) + proxy_url = f"http://{proxy_auth}{proxy_host}" + return NetworkSession( + network_client=self.network_client, + additional_headers=self.additional_headers, + base_urls=self.base_urls, + proxy_url=proxy_url, + retry_strategy=self.retry_strategy, + data_sanitizer=self.data_sanitizer, + ) + + def with_network_client(self, network_client: NetworkClient) -> 'NetworkSession': + """ + Generate a fresh network session by duplicating the existing configuration and network parameters, + while also including a new network client to be used for each API call. + :param network_client: NetworkClient object, which contains the fetch method + :return: a new instance of NetworkSession + """ + return NetworkSession( + network_client=network_client, + additional_headers=self.additional_headers, + base_urls=self.base_urls, + proxy_url=self.proxy_url, + retry_strategy=self.retry_strategy, + data_sanitizer=self.data_sanitizer, + ) + + def with_retry_strategy(self, retry_strategy: RetryStrategy) -> 'NetworkSession': + """ + Generate a fresh network session by duplicating the existing configuration and network parameters, + while also including a new retry options to be used for each API call. + :param retry_strategy: RetryStrategy object, which contains the retry logic + :return: a new instance of NetworkSession + """ + return NetworkSession( + network_client=self.network_client, + additional_headers=self.additional_headers, + base_urls=self.base_urls, + proxy_url=self.proxy_url, + retry_strategy=retry_strategy, + data_sanitizer=self.data_sanitizer, + ) + + def with_data_sanitizer(self, data_sanitizer: DataSanitizer) -> 'NetworkSession': + """ + Generate a fresh network session by duplicating the existing configuration and network parameters, + while also applying data sanitizer to sanitize sensitive data for logging. + :param data_sanitizer: + :return: + """ + return NetworkSession( + network_client=self.network_client, + additional_headers=self.additional_headers, + base_urls=self.base_urls, + proxy_url=self.proxy_url, + retry_strategy=self.retry_strategy, + data_sanitizer=data_sanitizer, + ) diff --git a/box_sdk_gen/networking/network_client.py b/box_sdk_gen/networking/network_client.py new file mode 100644 index 000000000..789354a42 --- /dev/null +++ b/box_sdk_gen/networking/network_client.py @@ -0,0 +1,14 @@ +from abc import abstractmethod + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + + +class NetworkClient: + def __init__(self): + pass + + @abstractmethod + def fetch(self, options: FetchOptions) -> FetchResponse: + pass diff --git a/box_sdk_gen/networking/proxy_config.py b/box_sdk_gen/networking/proxy_config.py new file mode 100644 index 000000000..05d6f1298 --- /dev/null +++ b/box_sdk_gen/networking/proxy_config.py @@ -0,0 +1,14 @@ +from typing import Optional + + +class ProxyConfig: + def __init__( + self, + url: str, + *, + username: Optional[str] = None, + password: Optional[str] = None + ): + self.url = url + self.username = username + self.password = password diff --git a/box_sdk_gen/networking/retries.py b/box_sdk_gen/networking/retries.py new file mode 100644 index 000000000..4d9c6c257 --- /dev/null +++ b/box_sdk_gen/networking/retries.py @@ -0,0 +1,96 @@ +from abc import abstractmethod + +from typing import Optional + +from box_sdk_gen.networking.fetch_options import FetchOptions + +from box_sdk_gen.networking.fetch_response import FetchResponse + +from box_sdk_gen.internal.utils import random + + +class RetryStrategy: + def __init__(self): + pass + + @abstractmethod + def should_retry( + self, + fetch_options: FetchOptions, + fetch_response: FetchResponse, + attempt_number: int, + ) -> bool: + pass + + @abstractmethod + def retry_after( + self, + fetch_options: FetchOptions, + fetch_response: FetchResponse, + attempt_number: int, + ) -> float: + pass + + +class BoxRetryStrategy(RetryStrategy): + def __init__( + self, + *, + max_attempts: int = 5, + retry_randomization_factor: float = 0.5, + retry_base_interval: float = 1, + max_retries_on_exception: int = 2, + **kwargs + ): + super().__init__(**kwargs) + self.max_attempts = max_attempts + self.retry_randomization_factor = retry_randomization_factor + self.retry_base_interval = retry_base_interval + self.max_retries_on_exception = max_retries_on_exception + + def should_retry( + self, + fetch_options: FetchOptions, + fetch_response: FetchResponse, + attempt_number: int, + ) -> bool: + if fetch_response.status == 0: + return attempt_number <= self.max_retries_on_exception + is_successful: bool = ( + fetch_response.status >= 200 and fetch_response.status < 400 + ) + retry_after_header: Optional[str] = fetch_response.headers.get('Retry-After') + is_accepted_with_retry_after: bool = ( + fetch_response.status == 202 and not retry_after_header == None + ) + if attempt_number >= self.max_attempts: + return False + if is_accepted_with_retry_after: + return True + if fetch_response.status >= 500: + return True + if fetch_response.status == 429: + return True + if fetch_response.status == 401 and not fetch_options.auth == None: + fetch_options.auth.refresh_token( + network_session=fetch_options.network_session + ) + return True + if is_successful: + return False + return False + + def retry_after( + self, + fetch_options: FetchOptions, + fetch_response: FetchResponse, + attempt_number: int, + ) -> float: + retry_after_header: Optional[str] = fetch_response.headers.get('Retry-After') + if not retry_after_header == None: + return float(retry_after_header) + randomization: float = random( + 1 - self.retry_randomization_factor, 1 + self.retry_randomization_factor + ) + exponential: float = 2**attempt_number + return (exponential * self.retry_base_interval) * randomization diff --git a/box_sdk_gen/networking/version.py b/box_sdk_gen/networking/version.py new file mode 100644 index 000000000..b794fd409 --- /dev/null +++ b/box_sdk_gen/networking/version.py @@ -0,0 +1 @@ +__version__ = '0.1.0' diff --git a/box_sdk_gen/parameters/__init__.py b/box_sdk_gen/parameters/__init__.py new file mode 100644 index 000000000..6ee9acb7e --- /dev/null +++ b/box_sdk_gen/parameters/__init__.py @@ -0,0 +1 @@ +from box_sdk_gen.parameters.v2025_r0 import * diff --git a/box_sdk_gen/parameters/v2025_r0/__init__.py b/box_sdk_gen/parameters/v2025_r0/__init__.py new file mode 100644 index 000000000..72c18cf87 --- /dev/null +++ b/box_sdk_gen/parameters/v2025_r0/__init__.py @@ -0,0 +1 @@ +from box_sdk_gen.parameters.v2025_r0.box_version_header_v2025_r0 import * diff --git a/box_sdk_gen/parameters/v2025_r0/box_version_header_v2025_r0.py b/box_sdk_gen/parameters/v2025_r0/box_version_header_v2025_r0.py new file mode 100644 index 000000000..ed66cfe5c --- /dev/null +++ b/box_sdk_gen/parameters/v2025_r0/box_version_header_v2025_r0.py @@ -0,0 +1,7 @@ +from enum import Enum + +from box_sdk_gen.box.errors import BoxSDKError + + +class BoxVersionHeaderV2025R0(str, Enum): + _2025_0 = '2025.0' diff --git a/box_sdk_gen/schemas/__init__.py b/box_sdk_gen/schemas/__init__.py new file mode 100644 index 000000000..20fab2585 --- /dev/null +++ b/box_sdk_gen/schemas/__init__.py @@ -0,0 +1,551 @@ +from box_sdk_gen.schemas.ai_agent_info import * + +from box_sdk_gen.schemas.ai_response import * + +from box_sdk_gen.schemas.ai_agent_reference import * + +from box_sdk_gen.schemas.ai_citation import * + +from box_sdk_gen.schemas.ai_response_full import * + +from box_sdk_gen.schemas.ai_dialogue_history import * + +from box_sdk_gen.schemas.ai_extract_response import * + +from box_sdk_gen.schemas.ai_extract_structured_response import * + +from box_sdk_gen.schemas.ai_item_base import * + +from box_sdk_gen.schemas.ai_item_ask import * + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import * + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import * + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import * + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import * + +from box_sdk_gen.schemas.ai_llm_endpoint_params import * + +from box_sdk_gen.schemas.ai_agent_spreadsheet_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_spreadsheet_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_spreadsheet_tool_response import * + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import * + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_text_gen import * + +from box_sdk_gen.schemas.ai_agent_long_text_tool_text_gen import * + +from box_sdk_gen.schemas.ai_agent_basic_gen_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_basic_gen_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_text_gen import * + +from box_sdk_gen.schemas.ai_studio_agent_basic_gen_tool_response import * + +from box_sdk_gen.schemas.ai_studio_agent_text_gen_response import * + +from box_sdk_gen.schemas.ai_agent_text_gen import * + +from box_sdk_gen.schemas.ai_text_gen_agent import * + +from box_sdk_gen.schemas.ai_text_gen import * + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_basic_text_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_basic_text_tool_response import * + +from box_sdk_gen.schemas.ai_agent_long_text_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_long_text_tool import * + +from box_sdk_gen.schemas.ai_studio_agent_long_text_tool_response import * + +from box_sdk_gen.schemas.ai_studio_agent_extract_response import * + +from box_sdk_gen.schemas.ai_studio_agent_ask_response import * + +from box_sdk_gen.schemas.ai_studio_agent_extract import * + +from box_sdk_gen.schemas.ai_studio_agent_ask import * + +from box_sdk_gen.schemas.ai_agent_extract_structured import * + +from box_sdk_gen.schemas.ai_extract_structured_agent import * + +from box_sdk_gen.schemas.ai_extract_structured import * + +from box_sdk_gen.schemas.ai_agent_extract import * + +from box_sdk_gen.schemas.ai_extract_agent import * + +from box_sdk_gen.schemas.ai_extract import * + +from box_sdk_gen.schemas.ai_agent_ask import * + +from box_sdk_gen.schemas.ai_ask_agent import * + +from box_sdk_gen.schemas.ai_ask import * + +from box_sdk_gen.schemas.ai_agent import * + +from box_sdk_gen.schemas.app_item import * + +from box_sdk_gen.schemas.classification import * + +from box_sdk_gen.schemas.classification_template import * + +from box_sdk_gen.schemas.client_error import * + +from box_sdk_gen.schemas.collaboration_allowlist_entry import * + +from box_sdk_gen.schemas.collaboration_allowlist_entries import * + +from box_sdk_gen.schemas.collaborator_variable import * + +from box_sdk_gen.schemas.collection import * + +from box_sdk_gen.schemas.collections import * + +from box_sdk_gen.schemas.comment_base import * + +from box_sdk_gen.schemas.completion_rule_variable import * + +from box_sdk_gen.schemas.email_alias import * + +from box_sdk_gen.schemas.email_aliases import * + +from box_sdk_gen.schemas.enterprise_base import * + +from box_sdk_gen.schemas.file_base import * + +from box_sdk_gen.schemas.file_request_update_request import * + +from box_sdk_gen.schemas.file_request_copy_request import * + +from box_sdk_gen.schemas.file_version_base import * + +from box_sdk_gen.schemas.file_version_mini import * + +from box_sdk_gen.schemas.file_mini import * + +from box_sdk_gen.schemas.files_under_retention import * + +from box_sdk_gen.schemas.files_on_hold import * + +from box_sdk_gen.schemas.file_conflict import * + +from box_sdk_gen.schemas.conflict_error import * + +from box_sdk_gen.schemas.folder_base import * + +from box_sdk_gen.schemas.folder_mini import * + +from box_sdk_gen.schemas.resource import * + +from box_sdk_gen.schemas.resource_scope import * + +from box_sdk_gen.schemas.access_token import * + +from box_sdk_gen.schemas.folder_reference import * + +from box_sdk_gen.schemas.generic_source import * + +from box_sdk_gen.schemas.group_base import * + +from box_sdk_gen.schemas.group_mini import * + +from box_sdk_gen.schemas.group import * + +from box_sdk_gen.schemas.group_full import * + +from box_sdk_gen.schemas.groups import * + +from box_sdk_gen.schemas.integration_mapping_base import * + +from box_sdk_gen.schemas.integration_mapping_box_item_slack import * + +from box_sdk_gen.schemas.integration_mapping_partner_item_slack import * + +from box_sdk_gen.schemas.integration_mapping_partner_item_teams import * + +from box_sdk_gen.schemas.integration_mapping_teams import * + +from box_sdk_gen.schemas.integration_mappings_teams import * + +from box_sdk_gen.schemas.integration_mapping_partner_item_teams_create_request import * + +from box_sdk_gen.schemas.integration_mapping_teams_create_request import * + +from box_sdk_gen.schemas.integration_mapping_slack_options import * + +from box_sdk_gen.schemas.integration_mapping_slack_create_request import * + +from box_sdk_gen.schemas.keyword_skill_card import * + +from box_sdk_gen.schemas.legal_hold_policy_mini import * + +from box_sdk_gen.schemas.legal_hold_policy_assignment_base import * + +from box_sdk_gen.schemas.metadata_base import * + +from box_sdk_gen.schemas.metadata import * + +from box_sdk_gen.schemas.metadatas import * + +from box_sdk_gen.schemas.metadata_full import * + +from box_sdk_gen.schemas.metadata_cascade_policy import * + +from box_sdk_gen.schemas.metadata_cascade_policies import * + +from box_sdk_gen.schemas.metadata_error import * + +from box_sdk_gen.schemas.metadata_field_filter_date_range import * + +from box_sdk_gen.schemas.metadata_field_filter_float_range import * + +from box_sdk_gen.schemas.metadata_filter_value import * + +from box_sdk_gen.schemas.metadata_filter import * + +from box_sdk_gen.schemas.metadata_instance_value import * + +from box_sdk_gen.schemas.metadata_query import * + +from box_sdk_gen.schemas.metadata_template import * + +from box_sdk_gen.schemas.metadata_templates import * + +from box_sdk_gen.schemas.o_auth_2_error import * + +from box_sdk_gen.schemas.post_o_auth_2_revoke import * + +from box_sdk_gen.schemas.post_o_auth_2_token import * + +from box_sdk_gen.schemas.post_o_auth_2_token_refresh_access_token import * + +from box_sdk_gen.schemas.realtime_server import * + +from box_sdk_gen.schemas.realtime_servers import * + +from box_sdk_gen.schemas.retention_policy_base import * + +from box_sdk_gen.schemas.retention_policy_mini import * + +from box_sdk_gen.schemas.file_version_retention import * + +from box_sdk_gen.schemas.file_version_retentions import * + +from box_sdk_gen.schemas.role_variable import * + +from box_sdk_gen.schemas.outcome import * + +from box_sdk_gen.schemas.session_termination_message import * + +from box_sdk_gen.schemas.shield_information_barrier_base import * + +from box_sdk_gen.schemas.shield_information_barrier_reference import * + +from box_sdk_gen.schemas.shield_information_barrier_report_base import * + +from box_sdk_gen.schemas.shield_information_barrier_report_details import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_member_base import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_base import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_mini import * + +from box_sdk_gen.schemas.sign_request_create_signer import * + +from box_sdk_gen.schemas.sign_request_prefill_tag import * + +from box_sdk_gen.schemas.template_signer_input import * + +from box_sdk_gen.schemas.template_signer import * + +from box_sdk_gen.schemas.sign_template import * + +from box_sdk_gen.schemas.sign_templates import * + +from box_sdk_gen.schemas.sign_request_signer_input import * + +from box_sdk_gen.schemas.sign_request_signer import * + +from box_sdk_gen.schemas.sign_request_base import * + +from box_sdk_gen.schemas.sign_request_create_request import * + +from box_sdk_gen.schemas.sign_request import * + +from box_sdk_gen.schemas.sign_requests import * + +from box_sdk_gen.schemas.status_skill_card import * + +from box_sdk_gen.schemas.storage_policy_mini import * + +from box_sdk_gen.schemas.storage_policy_assignment import * + +from box_sdk_gen.schemas.storage_policy_assignments import * + +from box_sdk_gen.schemas.storage_policy import * + +from box_sdk_gen.schemas.storage_policies import * + +from box_sdk_gen.schemas.terms_of_service_base import * + +from box_sdk_gen.schemas.terms_of_service import * + +from box_sdk_gen.schemas.terms_of_services import * + +from box_sdk_gen.schemas.timeline_skill_card import * + +from box_sdk_gen.schemas.tracking_code import * + +from box_sdk_gen.schemas.transcript_skill_card import * + +from box_sdk_gen.schemas.skill_card import * + +from box_sdk_gen.schemas.skill_cards_metadata import * + +from box_sdk_gen.schemas.upload_part_mini import * + +from box_sdk_gen.schemas.upload_part import * + +from box_sdk_gen.schemas.uploaded_part import * + +from box_sdk_gen.schemas.upload_parts import * + +from box_sdk_gen.schemas.upload_session import * + +from box_sdk_gen.schemas.upload_url import * + +from box_sdk_gen.schemas.user_base import * + +from box_sdk_gen.schemas.user_integration_mappings import * + +from box_sdk_gen.schemas.integration_mapping import * + +from box_sdk_gen.schemas.integration_mappings import * + +from box_sdk_gen.schemas.user_mini import * + +from box_sdk_gen.schemas.user import * + +from box_sdk_gen.schemas.user_full import * + +from box_sdk_gen.schemas.users import * + +from box_sdk_gen.schemas.trash_web_link_restored import * + +from box_sdk_gen.schemas.trash_web_link import * + +from box_sdk_gen.schemas.trash_folder_restored import * + +from box_sdk_gen.schemas.trash_folder import * + +from box_sdk_gen.schemas.trash_file_restored import * + +from box_sdk_gen.schemas.trash_file import * + +from box_sdk_gen.schemas.terms_of_service_user_status import * + +from box_sdk_gen.schemas.terms_of_service_user_statuses import * + +from box_sdk_gen.schemas.task_assignment import * + +from box_sdk_gen.schemas.task_assignments import * + +from box_sdk_gen.schemas.task import * + +from box_sdk_gen.schemas.tasks import * + +from box_sdk_gen.schemas.retention_policy_assignment import * + +from box_sdk_gen.schemas.retention_policy_assignments import * + +from box_sdk_gen.schemas.retention_policy import * + +from box_sdk_gen.schemas.retention_policies import * + +from box_sdk_gen.schemas.legal_hold_policy import * + +from box_sdk_gen.schemas.legal_hold_policies import * + +from box_sdk_gen.schemas.invite import * + +from box_sdk_gen.schemas.group_membership import * + +from box_sdk_gen.schemas.group_memberships import * + +from box_sdk_gen.schemas.file_version import * + +from box_sdk_gen.schemas.file_version_full import * + +from box_sdk_gen.schemas.file_versions import * + +from box_sdk_gen.schemas.file_request import * + +from box_sdk_gen.schemas.file import * + +from box_sdk_gen.schemas.file_full import * + +from box_sdk_gen.schemas.files import * + +from box_sdk_gen.schemas.event_source import * + +from box_sdk_gen.schemas.device_pinner import * + +from box_sdk_gen.schemas.device_pinners import * + +from box_sdk_gen.schemas.comment import * + +from box_sdk_gen.schemas.comment_full import * + +from box_sdk_gen.schemas.comments import * + +from box_sdk_gen.schemas.collaboration_allowlist_exempt_target import * + +from box_sdk_gen.schemas.collaboration_allowlist_exempt_targets import * + +from box_sdk_gen.schemas.app_item_event_source import * + +from box_sdk_gen.schemas.user_collaborations import * + +from box_sdk_gen.schemas.collaboration_access_grantee import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_restrictions import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_member_mini import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_member import * + +from box_sdk_gen.schemas.shield_information_barrier_segment_members import * + +from box_sdk_gen.schemas.shield_information_barrier_segment import * + +from box_sdk_gen.schemas.shield_information_barrier_segments import * + +from box_sdk_gen.schemas.shield_information_barrier_report import * + +from box_sdk_gen.schemas.shield_information_barrier_reports import * + +from box_sdk_gen.schemas.shield_information_barrier import * + +from box_sdk_gen.schemas.shield_information_barriers import * + +from box_sdk_gen.schemas.folder_lock import * + +from box_sdk_gen.schemas.folder_locks import * + +from box_sdk_gen.schemas.ai_agent_allowed_entity import * + +from box_sdk_gen.schemas.create_ai_agent import * + +from box_sdk_gen.schemas.ai_single_agent_response import * + +from box_sdk_gen.schemas.ai_single_agent_response_full import * + +from box_sdk_gen.schemas.ai_multiple_agent_response import * + +from box_sdk_gen.schemas.user_avatar import * + +from box_sdk_gen.schemas.watermark import * + +from box_sdk_gen.schemas.web_link_base import * + +from box_sdk_gen.schemas.web_link_mini import * + +from box_sdk_gen.schemas.web_link import * + +from box_sdk_gen.schemas.item import * + +from box_sdk_gen.schemas.items_offset_paginated import * + +from box_sdk_gen.schemas.items import * + +from box_sdk_gen.schemas.folder import * + +from box_sdk_gen.schemas.legal_hold_policy_assigned_item import * + +from box_sdk_gen.schemas.legal_hold_policy_assignment import * + +from box_sdk_gen.schemas.legal_hold_policy_assignments import * + +from box_sdk_gen.schemas.file_version_legal_hold import * + +from box_sdk_gen.schemas.file_version_legal_holds import * + +from box_sdk_gen.schemas.folder_full import * + +from box_sdk_gen.schemas.search_result_with_shared_link_item import * + +from box_sdk_gen.schemas.search_result_with_shared_link import * + +from box_sdk_gen.schemas.search_results_with_shared_links import * + +from box_sdk_gen.schemas.search_result_item import * + +from box_sdk_gen.schemas.search_results import * + +from box_sdk_gen.schemas.search_results_response import * + +from box_sdk_gen.schemas.recent_item_resource import * + +from box_sdk_gen.schemas.recent_item import * + +from box_sdk_gen.schemas.recent_items import * + +from box_sdk_gen.schemas.metadata_query_result_item import * + +from box_sdk_gen.schemas.metadata_query_results import * + +from box_sdk_gen.schemas.event_source_resource import * + +from box_sdk_gen.schemas.event import * + +from box_sdk_gen.schemas.events import * + +from box_sdk_gen.schemas.collaboration_item import * + +from box_sdk_gen.schemas.collaboration import * + +from box_sdk_gen.schemas.collaborations_offset_paginated import * + +from box_sdk_gen.schemas.collaborations import * + +from box_sdk_gen.schemas.app_item_associated_item import * + +from box_sdk_gen.schemas.app_item_association import * + +from box_sdk_gen.schemas.app_item_associations import * + +from box_sdk_gen.schemas.webhook_mini import * + +from box_sdk_gen.schemas.webhooks import * + +from box_sdk_gen.schemas.webhook import * + +from box_sdk_gen.schemas.workflow_mini import * + +from box_sdk_gen.schemas.workflow import * + +from box_sdk_gen.schemas.workflows import * + +from box_sdk_gen.schemas.zip_download import * + +from box_sdk_gen.schemas.zip_download_request import * + +from box_sdk_gen.schemas.zip_download_status import * + +from box_sdk_gen.schemas.v2025_r0 import * diff --git a/box_sdk_gen/schemas/access_token.py b/box_sdk_gen/schemas/access_token.py new file mode 100644 index 000000000..8ef1567b9 --- /dev/null +++ b/box_sdk_gen/schemas/access_token.py @@ -0,0 +1,60 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.resource_scope import ResourceScope + +from box_sdk_gen.box.errors import BoxSDKError + + +class AccessTokenTokenTypeField(str, Enum): + BEARER = 'bearer' + + +class AccessTokenIssuedTokenTypeField(str, Enum): + URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN = ( + 'urn:ietf:params:oauth:token-type:access_token' + ) + + +class AccessToken(BaseObject): + def __init__( + self, + *, + access_token: Optional[str] = None, + expires_in: Optional[int] = None, + token_type: Optional[AccessTokenTokenTypeField] = None, + restricted_to: Optional[List[ResourceScope]] = None, + refresh_token: Optional[str] = None, + issued_token_type: Optional[AccessTokenIssuedTokenTypeField] = None, + **kwargs + ): + """ + :param access_token: The requested access token., defaults to None + :type access_token: Optional[str], optional + :param expires_in: The time in seconds by which this token will expire., defaults to None + :type expires_in: Optional[int], optional + :param token_type: The type of access token returned., defaults to None + :type token_type: Optional[AccessTokenTokenTypeField], optional + :param restricted_to: The permissions that this access token permits, + providing a list of resources (files, folders, etc) + and the scopes permitted for each of those resources., defaults to None + :type restricted_to: Optional[List[ResourceScope]], optional + :param refresh_token: The refresh token for this access token, which can be used + to request a new access token when the current one expires., defaults to None + :type refresh_token: Optional[str], optional + :param issued_token_type: The type of downscoped access token returned. This is only + returned if an access token has been downscoped., defaults to None + :type issued_token_type: Optional[AccessTokenIssuedTokenTypeField], optional + """ + super().__init__(**kwargs) + self.access_token = access_token + self.expires_in = expires_in + self.token_type = token_type + self.restricted_to = restricted_to + self.refresh_token = refresh_token + self.issued_token_type = issued_token_type diff --git a/box_sdk_gen/schemas/ai_agent.py b/box_sdk_gen/schemas/ai_agent.py new file mode 100644 index 000000000..b0248a7b4 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent.py @@ -0,0 +1,13 @@ +from typing import Union + +from box_sdk_gen.schemas.ai_agent_ask import AiAgentAsk + +from box_sdk_gen.schemas.ai_agent_text_gen import AiAgentTextGen + +from box_sdk_gen.schemas.ai_agent_extract import AiAgentExtract + +from box_sdk_gen.schemas.ai_agent_extract_structured import AiAgentExtractStructured + +from box_sdk_gen.box.errors import BoxSDKError + +AiAgent = Union[AiAgentAsk, AiAgentTextGen, AiAgentExtract, AiAgentExtractStructured] diff --git a/box_sdk_gen/schemas/ai_agent_allowed_entity.py b/box_sdk_gen/schemas/ai_agent_allowed_entity.py new file mode 100644 index 000000000..202cf8649 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_allowed_entity.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.box.errors import BoxSDKError + +AiAgentAllowedEntity = Union[UserBase, GroupBase] diff --git a/box_sdk_gen/schemas/ai_agent_ask.py b/box_sdk_gen/schemas/ai_agent_ask.py new file mode 100644 index 000000000..1193998b5 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_ask.py @@ -0,0 +1,48 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_agent_long_text_tool import AiAgentLongTextTool + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.schemas.ai_agent_spreadsheet_tool import AiAgentSpreadsheetTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentAskTypeField(str, Enum): + AI_AGENT_ASK = 'ai_agent_ask' + + +class AiAgentAsk(BaseObject): + _discriminator = 'type', {'ai_agent_ask'} + + def __init__( + self, + *, + type: AiAgentAskTypeField = AiAgentAskTypeField.AI_AGENT_ASK, + long_text: Optional[AiAgentLongTextTool] = None, + basic_text: Optional[AiAgentBasicTextTool] = None, + spreadsheet: Optional[AiAgentSpreadsheetTool] = None, + long_text_multi: Optional[AiAgentLongTextTool] = None, + basic_text_multi: Optional[AiAgentBasicTextTool] = None, + basic_image: Optional[AiAgentBasicTextTool] = None, + basic_image_multi: Optional[AiAgentBasicTextTool] = None, + **kwargs + ): + """ + :param type: The type of AI agent used to handle queries., defaults to AiAgentAskTypeField.AI_AGENT_ASK + :type type: AiAgentAskTypeField, optional + """ + super().__init__(**kwargs) + self.type = type + self.long_text = long_text + self.basic_text = basic_text + self.spreadsheet = spreadsheet + self.long_text_multi = long_text_multi + self.basic_text_multi = basic_text_multi + self.basic_image = basic_image + self.basic_image_multi = basic_image_multi diff --git a/box_sdk_gen/schemas/ai_agent_basic_gen_tool.py b/box_sdk_gen/schemas/ai_agent_basic_gen_tool.py new file mode 100644 index 000000000..6aef8bb0c --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_basic_gen_tool.py @@ -0,0 +1,69 @@ +from typing import Optional + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_text_gen import ( + AiAgentBasicTextToolTextGen, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool_text_gen import ( + AiAgentLongTextToolTextGenEmbeddingsField, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool_text_gen import ( + AiAgentLongTextToolTextGen, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentBasicGenTool(AiAgentLongTextToolTextGen): + def __init__( + self, + *, + content_template: Optional[str] = None, + embeddings: Optional[AiAgentLongTextToolTextGenEmbeddingsField] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param content_template: How the content should be included in a request to the LLM. + Input for `{content}` is optional, depending on the use., defaults to None + :type content_template: Optional[str], optional + :param system_message: System messages aim at helping the LLM understand its role and what it is supposed to do. + The input for `{current_date}` is optional, depending on the use., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + + When using the `prompt_template` parameter, you **must include** input for `{user_question}`. + Inputs for `{current_date}` and `{content}` are optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + embeddings=embeddings, + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.content_template = content_template diff --git a/box_sdk_gen/schemas/ai_agent_basic_text_tool.py b/box_sdk_gen/schemas/ai_agent_basic_text_tool.py new file mode 100644 index 000000000..5c48ae509 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_basic_text_tool.py @@ -0,0 +1,48 @@ +from typing import Optional + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentBasicTextTool(AiAgentBasicTextToolBase): + def __init__( + self, + *, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param system_message: System messages try to help the LLM "understand" its role and what it is supposed to do., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + When passing `prompt_template` parameters, you **must include** inputs for `{user_question}` and `{content}`. + `{current_date}` is optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.system_message = system_message + self.prompt_template = prompt_template diff --git a/box_sdk_gen/schemas/ai_agent_basic_text_tool_base.py b/box_sdk_gen/schemas/ai_agent_basic_text_tool_base.py new file mode 100644 index 000000000..0090d4218 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_basic_text_tool_base.py @@ -0,0 +1,36 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentBasicTextToolBase(BaseObject): + def __init__( + self, + *, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__(**kwargs) + self.model = model + self.num_tokens_for_completion = num_tokens_for_completion + self.llm_endpoint_params = llm_endpoint_params diff --git a/box_sdk_gen/schemas/ai_agent_basic_text_tool_text_gen.py b/box_sdk_gen/schemas/ai_agent_basic_text_tool_text_gen.py new file mode 100644 index 000000000..2d9a73f01 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_basic_text_tool_text_gen.py @@ -0,0 +1,50 @@ +from typing import Optional + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentBasicTextToolTextGen(AiAgentBasicTextToolBase): + def __init__( + self, + *, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param system_message: System messages aim at helping the LLM understand its role and what it is supposed to do. + The input for `{current_date}` is optional, depending on the use., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + + When using the `prompt_template` parameter, you **must include** input for `{user_question}`. + Inputs for `{current_date}` and `{content}` are optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.system_message = system_message + self.prompt_template = prompt_template diff --git a/box_sdk_gen/schemas/ai_agent_extract.py b/box_sdk_gen/schemas/ai_agent_extract.py new file mode 100644 index 000000000..02a2e34ce --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_extract.py @@ -0,0 +1,38 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_agent_long_text_tool import AiAgentLongTextTool + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentExtractTypeField(str, Enum): + AI_AGENT_EXTRACT = 'ai_agent_extract' + + +class AiAgentExtract(BaseObject): + _discriminator = 'type', {'ai_agent_extract'} + + def __init__( + self, + *, + type: AiAgentExtractTypeField = AiAgentExtractTypeField.AI_AGENT_EXTRACT, + long_text: Optional[AiAgentLongTextTool] = None, + basic_text: Optional[AiAgentBasicTextTool] = None, + basic_image: Optional[AiAgentBasicTextTool] = None, + **kwargs + ): + """ + :param type: The type of AI agent to be used for extraction., defaults to AiAgentExtractTypeField.AI_AGENT_EXTRACT + :type type: AiAgentExtractTypeField, optional + """ + super().__init__(**kwargs) + self.type = type + self.long_text = long_text + self.basic_text = basic_text + self.basic_image = basic_image diff --git a/box_sdk_gen/schemas/ai_agent_extract_structured.py b/box_sdk_gen/schemas/ai_agent_extract_structured.py new file mode 100644 index 000000000..142ae9961 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_extract_structured.py @@ -0,0 +1,38 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_agent_long_text_tool import AiAgentLongTextTool + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentExtractStructuredTypeField(str, Enum): + AI_AGENT_EXTRACT_STRUCTURED = 'ai_agent_extract_structured' + + +class AiAgentExtractStructured(BaseObject): + _discriminator = 'type', {'ai_agent_extract_structured'} + + def __init__( + self, + *, + type: AiAgentExtractStructuredTypeField = AiAgentExtractStructuredTypeField.AI_AGENT_EXTRACT_STRUCTURED, + long_text: Optional[AiAgentLongTextTool] = None, + basic_text: Optional[AiAgentBasicTextTool] = None, + basic_image: Optional[AiAgentBasicTextTool] = None, + **kwargs + ): + """ + :param type: The type of AI agent to be used for extraction., defaults to AiAgentExtractStructuredTypeField.AI_AGENT_EXTRACT_STRUCTURED + :type type: AiAgentExtractStructuredTypeField, optional + """ + super().__init__(**kwargs) + self.type = type + self.long_text = long_text + self.basic_text = basic_text + self.basic_image = basic_image diff --git a/box_sdk_gen/schemas/ai_agent_info.py b/box_sdk_gen/schemas/ai_agent_info.py new file mode 100644 index 000000000..cca80e8f4 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_info.py @@ -0,0 +1,49 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentInfoModelsField(BaseObject): + def __init__( + self, + *, + name: Optional[str] = None, + provider: Optional[str] = None, + supported_purpose: Optional[str] = None, + **kwargs + ): + """ + :param name: The name of the model used for the request., defaults to None + :type name: Optional[str], optional + :param provider: The provider that owns the model used for the request., defaults to None + :type provider: Optional[str], optional + :param supported_purpose: The supported purpose utilized by the model used for the request., defaults to None + :type supported_purpose: Optional[str], optional + """ + super().__init__(**kwargs) + self.name = name + self.provider = provider + self.supported_purpose = supported_purpose + + +class AiAgentInfo(BaseObject): + def __init__( + self, + *, + models: Optional[List[AiAgentInfoModelsField]] = None, + processor: Optional[str] = None, + **kwargs + ): + """ + :param models: The models used for the request., defaults to None + :type models: Optional[List[AiAgentInfoModelsField]], optional + :param processor: The processor used for the request., defaults to None + :type processor: Optional[str], optional + """ + super().__init__(**kwargs) + self.models = models + self.processor = processor diff --git a/box_sdk_gen/schemas/ai_agent_long_text_tool.py b/box_sdk_gen/schemas/ai_agent_long_text_tool.py new file mode 100644 index 000000000..57773078c --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_long_text_tool.py @@ -0,0 +1,90 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentLongTextToolEmbeddingsStrategyField(BaseObject): + def __init__( + self, + *, + id: Optional[str] = None, + num_tokens_per_chunk: Optional[int] = None, + **kwargs + ): + """ + :param id: The strategy used for the AI agent for calculating embeddings., defaults to None + :type id: Optional[str], optional + :param num_tokens_per_chunk: The number of tokens per chunk., defaults to None + :type num_tokens_per_chunk: Optional[int], optional + """ + super().__init__(**kwargs) + self.id = id + self.num_tokens_per_chunk = num_tokens_per_chunk + + +class AiAgentLongTextToolEmbeddingsField(BaseObject): + def __init__( + self, + *, + model: Optional[str] = None, + strategy: Optional[AiAgentLongTextToolEmbeddingsStrategyField] = None, + **kwargs + ): + """ + :param model: The model used for the AI agent for calculating embeddings., defaults to None + :type model: Optional[str], optional + """ + super().__init__(**kwargs) + self.model = model + self.strategy = strategy + + +class AiAgentLongTextTool(AiAgentBasicTextTool): + def __init__( + self, + *, + embeddings: Optional[AiAgentLongTextToolEmbeddingsField] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param system_message: System messages try to help the LLM "understand" its role and what it is supposed to do., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + When passing `prompt_template` parameters, you **must include** inputs for `{user_question}` and `{content}`. + `{current_date}` is optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.embeddings = embeddings diff --git a/box_sdk_gen/schemas/ai_agent_long_text_tool_text_gen.py b/box_sdk_gen/schemas/ai_agent_long_text_tool_text_gen.py new file mode 100644 index 000000000..445162945 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_long_text_tool_text_gen.py @@ -0,0 +1,94 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_text_gen import ( + AiAgentBasicTextToolTextGen, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentLongTextToolTextGenEmbeddingsStrategyField(BaseObject): + def __init__( + self, + *, + id: Optional[str] = None, + num_tokens_per_chunk: Optional[int] = None, + **kwargs + ): + """ + :param id: The strategy used for the AI agent for calculating embeddings., defaults to None + :type id: Optional[str], optional + :param num_tokens_per_chunk: The number of tokens per chunk., defaults to None + :type num_tokens_per_chunk: Optional[int], optional + """ + super().__init__(**kwargs) + self.id = id + self.num_tokens_per_chunk = num_tokens_per_chunk + + +class AiAgentLongTextToolTextGenEmbeddingsField(BaseObject): + def __init__( + self, + *, + model: Optional[str] = None, + strategy: Optional[AiAgentLongTextToolTextGenEmbeddingsStrategyField] = None, + **kwargs + ): + """ + :param model: The model used for the AI agent for calculating embeddings., defaults to None + :type model: Optional[str], optional + """ + super().__init__(**kwargs) + self.model = model + self.strategy = strategy + + +class AiAgentLongTextToolTextGen(AiAgentBasicTextToolTextGen): + def __init__( + self, + *, + embeddings: Optional[AiAgentLongTextToolTextGenEmbeddingsField] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param system_message: System messages aim at helping the LLM understand its role and what it is supposed to do. + The input for `{current_date}` is optional, depending on the use., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + + When using the `prompt_template` parameter, you **must include** input for `{user_question}`. + Inputs for `{current_date}` and `{content}` are optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.embeddings = embeddings diff --git a/box_sdk_gen/schemas/ai_agent_reference.py b/box_sdk_gen/schemas/ai_agent_reference.py new file mode 100644 index 000000000..bf961999e --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_reference.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentReferenceTypeField(str, Enum): + AI_AGENT_ID = 'ai_agent_id' + + +class AiAgentReference(BaseObject): + _discriminator = 'type', {'ai_agent_id'} + + def __init__( + self, + *, + type: AiAgentReferenceTypeField = AiAgentReferenceTypeField.AI_AGENT_ID, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of AI agent used to handle queries., defaults to AiAgentReferenceTypeField.AI_AGENT_ID + :type type: AiAgentReferenceTypeField, optional + :param id: The ID of an Agent., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id diff --git a/box_sdk_gen/schemas/ai_agent_spreadsheet_tool.py b/box_sdk_gen/schemas/ai_agent_spreadsheet_tool.py new file mode 100644 index 000000000..fe7942124 --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_spreadsheet_tool.py @@ -0,0 +1,36 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentSpreadsheetTool(BaseObject): + def __init__( + self, + *, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param model: The model used for the AI agent for spreadsheets. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__(**kwargs) + self.model = model + self.num_tokens_for_completion = num_tokens_for_completion + self.llm_endpoint_params = llm_endpoint_params diff --git a/box_sdk_gen/schemas/ai_agent_text_gen.py b/box_sdk_gen/schemas/ai_agent_text_gen.py new file mode 100644 index 000000000..2b788eafe --- /dev/null +++ b/box_sdk_gen/schemas/ai_agent_text_gen.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_agent_basic_gen_tool import AiAgentBasicGenTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAgentTextGenTypeField(str, Enum): + AI_AGENT_TEXT_GEN = 'ai_agent_text_gen' + + +class AiAgentTextGen(BaseObject): + _discriminator = 'type', {'ai_agent_text_gen'} + + def __init__( + self, + *, + type: AiAgentTextGenTypeField = AiAgentTextGenTypeField.AI_AGENT_TEXT_GEN, + basic_gen: Optional[AiAgentBasicGenTool] = None, + **kwargs + ): + """ + :param type: The type of AI agent used for generating text., defaults to AiAgentTextGenTypeField.AI_AGENT_TEXT_GEN + :type type: AiAgentTextGenTypeField, optional + """ + super().__init__(**kwargs) + self.type = type + self.basic_gen = basic_gen diff --git a/box_sdk_gen/schemas/ai_ask.py b/box_sdk_gen/schemas/ai_ask.py new file mode 100644 index 000000000..d4a347357 --- /dev/null +++ b/box_sdk_gen/schemas/ai_ask.py @@ -0,0 +1,64 @@ +from enum import Enum + +from typing import List + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_ask import AiAgentAsk + +from box_sdk_gen.schemas.ai_item_ask import AiItemAsk + +from box_sdk_gen.schemas.ai_dialogue_history import AiDialogueHistory + +from box_sdk_gen.schemas.ai_ask_agent import AiAskAgent + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiAskModeField(str, Enum): + MULTIPLE_ITEM_QA = 'multiple_item_qa' + SINGLE_ITEM_QA = 'single_item_qa' + + +class AiAsk(BaseObject): + def __init__( + self, + mode: AiAskModeField, + prompt: str, + items: List[AiItemAsk], + *, + dialogue_history: Optional[List[AiDialogueHistory]] = None, + include_citations: Optional[bool] = None, + ai_agent: Optional[AiAskAgent] = None, + **kwargs + ): + """ + :param mode: Box AI handles text documents with text representations up to 1MB in size, or a maximum of 25 files, + whichever comes first. If the text file size exceeds 1MB, the first 1MB of text representation will be processed. + Box AI handles image documents with a resolution of 1024 x 1024 pixels, with a maximum of 5 images or 5 pages + for multi-page images. If the number of image or image pages exceeds 5, the first 5 images or pages will + be processed. If you set mode parameter to `single_item_qa`, the items array can have one element only. + Currently Box AI does not support multi-modal requests. If both images and text are sent Box AI will only + process the text. + :type mode: AiAskModeField + :param prompt: The prompt provided by the client to be answered by the LLM. + The prompt's length is limited to 10000 characters. + :type prompt: str + :param items: The items to be processed by the LLM, often files. + :type items: List[AiItemAsk] + :param dialogue_history: The history of prompts and answers previously passed to the LLM. This provides additional context to the LLM in generating the response., defaults to None + :type dialogue_history: Optional[List[AiDialogueHistory]], optional + :param include_citations: A flag to indicate whether citations should be returned., defaults to None + :type include_citations: Optional[bool], optional + """ + super().__init__(**kwargs) + self.mode = mode + self.prompt = prompt + self.items = items + self.dialogue_history = dialogue_history + self.include_citations = include_citations + self.ai_agent = ai_agent diff --git a/box_sdk_gen/schemas/ai_ask_agent.py b/box_sdk_gen/schemas/ai_ask_agent.py new file mode 100644 index 000000000..a7d4b049f --- /dev/null +++ b/box_sdk_gen/schemas/ai_ask_agent.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_ask import AiAgentAsk + +from box_sdk_gen.box.errors import BoxSDKError + +AiAskAgent = Union[AiAgentReference, AiAgentAsk] diff --git a/box_sdk_gen/schemas/ai_citation.py b/box_sdk_gen/schemas/ai_citation.py new file mode 100644 index 000000000..b90376e95 --- /dev/null +++ b/box_sdk_gen/schemas/ai_citation.py @@ -0,0 +1,40 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiCitationTypeField(str, Enum): + FILE = 'file' + + +class AiCitation(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + *, + content: Optional[str] = None, + id: Optional[str] = None, + type: Optional[AiCitationTypeField] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param content: The specific content from where the answer was referenced., defaults to None + :type content: Optional[str], optional + :param id: The id of the item., defaults to None + :type id: Optional[str], optional + :param type: The type of the item., defaults to None + :type type: Optional[AiCitationTypeField], optional + :param name: The name of the item., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.content = content + self.id = id + self.type = type + self.name = name diff --git a/box_sdk_gen/schemas/ai_dialogue_history.py b/box_sdk_gen/schemas/ai_dialogue_history.py new file mode 100644 index 000000000..778304ef7 --- /dev/null +++ b/box_sdk_gen/schemas/ai_dialogue_history.py @@ -0,0 +1,30 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class AiDialogueHistory(BaseObject): + def __init__( + self, + *, + prompt: Optional[str] = None, + answer: Optional[str] = None, + created_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param prompt: The prompt previously provided by the client and answered by the LLM., defaults to None + :type prompt: Optional[str], optional + :param answer: The answer previously provided by the LLM., defaults to None + :type answer: Optional[str], optional + :param created_at: The ISO date formatted timestamp of when the previous answer to the prompt was created., defaults to None + :type created_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.prompt = prompt + self.answer = answer + self.created_at = created_at diff --git a/box_sdk_gen/schemas/ai_extract.py b/box_sdk_gen/schemas/ai_extract.py new file mode 100644 index 000000000..5b0a0d258 --- /dev/null +++ b/box_sdk_gen/schemas/ai_extract.py @@ -0,0 +1,36 @@ +from typing import List + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_extract import AiAgentExtract + +from box_sdk_gen.schemas.ai_item_base import AiItemBase + +from box_sdk_gen.schemas.ai_extract_agent import AiExtractAgent + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiExtract(BaseObject): + def __init__( + self, + prompt: str, + items: List[AiItemBase], + *, + ai_agent: Optional[AiExtractAgent] = None, + **kwargs + ): + """ + :param prompt: The prompt provided to a Large Language Model (LLM) in the request. The prompt can be up to 10000 characters long and it can be an XML or a JSON schema. + :type prompt: str + :param items: The items that LLM will process. Currently, you can use files only. + :type items: List[AiItemBase] + """ + super().__init__(**kwargs) + self.prompt = prompt + self.items = items + self.ai_agent = ai_agent diff --git a/box_sdk_gen/schemas/ai_extract_agent.py b/box_sdk_gen/schemas/ai_extract_agent.py new file mode 100644 index 000000000..f06508724 --- /dev/null +++ b/box_sdk_gen/schemas/ai_extract_agent.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_extract import AiAgentExtract + +from box_sdk_gen.box.errors import BoxSDKError + +AiExtractAgent = Union[AiAgentReference, AiAgentExtract] diff --git a/box_sdk_gen/schemas/ai_extract_response.py b/box_sdk_gen/schemas/ai_extract_response.py new file mode 100644 index 000000000..e18207b35 --- /dev/null +++ b/box_sdk_gen/schemas/ai_extract_response.py @@ -0,0 +1,8 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiExtractResponse(BaseObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) diff --git a/box_sdk_gen/schemas/ai_extract_structured.py b/box_sdk_gen/schemas/ai_extract_structured.py new file mode 100644 index 000000000..5a742b529 --- /dev/null +++ b/box_sdk_gen/schemas/ai_extract_structured.py @@ -0,0 +1,134 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_extract_structured import AiAgentExtractStructured + +from box_sdk_gen.schemas.ai_item_base import AiItemBase + +from box_sdk_gen.schemas.ai_extract_structured_agent import AiExtractStructuredAgent + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiExtractStructuredMetadataTemplateTypeField(str, Enum): + METADATA_TEMPLATE = 'metadata_template' + + +class AiExtractStructuredMetadataTemplateField(BaseObject): + _discriminator = 'type', {'metadata_template'} + + def __init__( + self, + *, + template_key: Optional[str] = None, + type: Optional[AiExtractStructuredMetadataTemplateTypeField] = None, + scope: Optional[str] = None, + **kwargs + ): + """ + :param template_key: The name of the metadata template., defaults to None + :type template_key: Optional[str], optional + :param type: Value is always `metadata_template`., defaults to None + :type type: Optional[AiExtractStructuredMetadataTemplateTypeField], optional + :param scope: The scope of the metadata template that can either be global or + enterprise. + * The **global** scope is used for templates that are + available to any Box enterprise. + * The **enterprise** scope represents templates created within a specific enterprise, + containing the ID of that enterprise., defaults to None + :type scope: Optional[str], optional + """ + super().__init__(**kwargs) + self.template_key = template_key + self.type = type + self.scope = scope + + +class AiExtractStructuredFieldsOptionsField(BaseObject): + def __init__(self, key: str, **kwargs): + """ + :param key: A unique identifier for the field. + :type key: str + """ + super().__init__(**kwargs) + self.key = key + + +class AiExtractStructuredFieldsField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'display_name': 'displayName', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'displayName': 'display_name', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + key: str, + *, + description: Optional[str] = None, + display_name: Optional[str] = None, + prompt: Optional[str] = None, + type: Optional[str] = None, + options: Optional[List[AiExtractStructuredFieldsOptionsField]] = None, + **kwargs + ): + """ + :param key: A unique identifier for the field. + :type key: str + :param description: A description of the field., defaults to None + :type description: Optional[str], optional + :param display_name: The display name of the field., defaults to None + :type display_name: Optional[str], optional + :param prompt: The context about the key that may include how to find and format it., defaults to None + :type prompt: Optional[str], optional + :param type: The type of the field. It include but is not limited to string, float, date, enum, and multiSelect., defaults to None + :type type: Optional[str], optional + :param options: A list of options for this field. This is most often used in combination with the enum and multiSelect field types., defaults to None + :type options: Optional[List[AiExtractStructuredFieldsOptionsField]], optional + """ + super().__init__(**kwargs) + self.key = key + self.description = description + self.display_name = display_name + self.prompt = prompt + self.type = type + self.options = options + + +class AiExtractStructured(BaseObject): + def __init__( + self, + items: List[AiItemBase], + *, + metadata_template: Optional[AiExtractStructuredMetadataTemplateField] = None, + fields: Optional[List[AiExtractStructuredFieldsField]] = None, + ai_agent: Optional[AiExtractStructuredAgent] = None, + **kwargs + ): + """ + :param items: The items to be processed by the LLM. Currently you can use files only. + :type items: List[AiItemBase] + :param metadata_template: The metadata template containing the fields to extract. + For your request to work, you must provide either `metadata_template` or `fields`, but not both., defaults to None + :type metadata_template: Optional[AiExtractStructuredMetadataTemplateField], optional + :param fields: The fields to be extracted from the provided items. + For your request to work, you must provide either `metadata_template` or `fields`, but not both., defaults to None + :type fields: Optional[List[AiExtractStructuredFieldsField]], optional + """ + super().__init__(**kwargs) + self.items = items + self.metadata_template = metadata_template + self.fields = fields + self.ai_agent = ai_agent diff --git a/box_sdk_gen/schemas/ai_extract_structured_agent.py b/box_sdk_gen/schemas/ai_extract_structured_agent.py new file mode 100644 index 000000000..76284f484 --- /dev/null +++ b/box_sdk_gen/schemas/ai_extract_structured_agent.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_extract_structured import AiAgentExtractStructured + +from box_sdk_gen.box.errors import BoxSDKError + +AiExtractStructuredAgent = Union[AiAgentReference, AiAgentExtractStructured] diff --git a/box_sdk_gen/schemas/ai_extract_structured_response.py b/box_sdk_gen/schemas/ai_extract_structured_response.py new file mode 100644 index 000000000..9c9e7f3ed --- /dev/null +++ b/box_sdk_gen/schemas/ai_extract_structured_response.py @@ -0,0 +1,34 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_extract_response import AiExtractResponse + +from box_sdk_gen.schemas.ai_agent_info import AiAgentInfo + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class AiExtractStructuredResponse(BaseObject): + def __init__( + self, + answer: AiExtractResponse, + created_at: DateTime, + *, + completion_reason: Optional[str] = None, + ai_agent_info: Optional[AiAgentInfo] = None, + **kwargs + ): + """ + :param created_at: The ISO date formatted timestamp of when the answer to the prompt was created. + :type created_at: DateTime + :param completion_reason: The reason the response finishes., defaults to None + :type completion_reason: Optional[str], optional + """ + super().__init__(**kwargs) + self.answer = answer + self.created_at = created_at + self.completion_reason = completion_reason + self.ai_agent_info = ai_agent_info diff --git a/box_sdk_gen/schemas/ai_item_ask.py b/box_sdk_gen/schemas/ai_item_ask.py new file mode 100644 index 000000000..d2702b88c --- /dev/null +++ b/box_sdk_gen/schemas/ai_item_ask.py @@ -0,0 +1,37 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiItemAskTypeField(str, Enum): + FILE = 'file' + HUBS = 'hubs' + + +class AiItemAsk(BaseObject): + _discriminator = 'type', {'file', 'hubs'} + + def __init__( + self, + id: str, + type: AiItemAskTypeField, + *, + content: Optional[str] = None, + **kwargs + ): + """ + :param id: The ID of the file. + :type id: str + :param type: The type of the item. A `hubs` item must be used as a single item. + :type type: AiItemAskTypeField + :param content: The content of the item, often the text representation., defaults to None + :type content: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.content = content diff --git a/box_sdk_gen/schemas/ai_item_base.py b/box_sdk_gen/schemas/ai_item_base.py new file mode 100644 index 000000000..53cd81822 --- /dev/null +++ b/box_sdk_gen/schemas/ai_item_base.py @@ -0,0 +1,36 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiItemBaseTypeField(str, Enum): + FILE = 'file' + + +class AiItemBase(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + id: str, + *, + type: AiItemBaseTypeField = AiItemBaseTypeField.FILE, + content: Optional[str] = None, + **kwargs + ): + """ + :param id: The ID of the file. + :type id: str + :param type: The type of the item. Currently the value can be `file` only., defaults to AiItemBaseTypeField.FILE + :type type: AiItemBaseTypeField, optional + :param content: The content of the item, often the text representation., defaults to None + :type content: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.content = content diff --git a/box_sdk_gen/schemas/ai_llm_endpoint_params.py b/box_sdk_gen/schemas/ai_llm_endpoint_params.py new file mode 100644 index 000000000..3425b0cd3 --- /dev/null +++ b/box_sdk_gen/schemas/ai_llm_endpoint_params.py @@ -0,0 +1,18 @@ +from typing import Union + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.box.errors import BoxSDKError + +AiLlmEndpointParams = Union[ + AiLlmEndpointParamsOpenAi, + AiLlmEndpointParamsGoogle, + AiLlmEndpointParamsAws, + AiLlmEndpointParamsIbm, +] diff --git a/box_sdk_gen/schemas/ai_llm_endpoint_params_aws.py b/box_sdk_gen/schemas/ai_llm_endpoint_params_aws.py new file mode 100644 index 000000000..c7ee05318 --- /dev/null +++ b/box_sdk_gen/schemas/ai_llm_endpoint_params_aws.py @@ -0,0 +1,41 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiLlmEndpointParamsAwsTypeField(str, Enum): + AWS_PARAMS = 'aws_params' + + +class AiLlmEndpointParamsAws(BaseObject): + _discriminator = 'type', {'aws_params'} + + def __init__( + self, + *, + type: AiLlmEndpointParamsAwsTypeField = AiLlmEndpointParamsAwsTypeField.AWS_PARAMS, + temperature: Optional[float] = None, + top_p: Optional[float] = None, + **kwargs + ): + """ + :param type: The type of the AI LLM endpoint params object for AWS. + This parameter is **required**., defaults to AiLlmEndpointParamsAwsTypeField.AWS_PARAMS + :type type: AiLlmEndpointParamsAwsTypeField, optional + :param temperature: What sampling temperature to use, between 0 and 1. Higher values like 0.8 will make the output more random, + while lower values like 0.2 will make it more focused and deterministic. + We generally recommend altering this or `top_p` but not both., defaults to None + :type temperature: Optional[float], optional + :param top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results + of the tokens with `top_p` probability mass. So 0.1 means only the tokens comprising the top 10% probability + mass are considered. We generally recommend altering this or temperature but not both., defaults to None + :type top_p: Optional[float], optional + """ + super().__init__(**kwargs) + self.type = type + self.temperature = temperature + self.top_p = top_p diff --git a/box_sdk_gen/schemas/ai_llm_endpoint_params_google.py b/box_sdk_gen/schemas/ai_llm_endpoint_params_google.py new file mode 100644 index 000000000..b30f28f4b --- /dev/null +++ b/box_sdk_gen/schemas/ai_llm_endpoint_params_google.py @@ -0,0 +1,43 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiLlmEndpointParamsGoogleTypeField(str, Enum): + GOOGLE_PARAMS = 'google_params' + + +class AiLlmEndpointParamsGoogle(BaseObject): + _discriminator = 'type', {'google_params'} + + def __init__( + self, + *, + type: AiLlmEndpointParamsGoogleTypeField = AiLlmEndpointParamsGoogleTypeField.GOOGLE_PARAMS, + temperature: Optional[float] = None, + top_p: Optional[float] = None, + top_k: Optional[float] = None, + **kwargs + ): + """ + :param type: The type of the AI LLM endpoint params object for Google. + This parameter is **required**., defaults to AiLlmEndpointParamsGoogleTypeField.GOOGLE_PARAMS + :type type: AiLlmEndpointParamsGoogleTypeField, optional + :param temperature: The temperature is used for sampling during response generation, which occurs when `top-P` and `top-K` are applied. Temperature controls the degree of randomness in the token selection., defaults to None + :type temperature: Optional[float], optional + :param top_p: `Top-P` changes how the model selects tokens for output. Tokens are selected from the most (see `top-K`) to least probable until the sum of their probabilities equals the `top-P` value., defaults to None + :type top_p: Optional[float], optional + :param top_k: `Top-K` changes how the model selects tokens for output. A low `top-K` means the next selected token is + the most probable among all tokens in the model's vocabulary (also called greedy decoding), + while a high `top-K` means that the next token is selected from among the three most probable tokens by using temperature., defaults to None + :type top_k: Optional[float], optional + """ + super().__init__(**kwargs) + self.type = type + self.temperature = temperature + self.top_p = top_p + self.top_k = top_k diff --git a/box_sdk_gen/schemas/ai_llm_endpoint_params_ibm.py b/box_sdk_gen/schemas/ai_llm_endpoint_params_ibm.py new file mode 100644 index 000000000..851bd34fe --- /dev/null +++ b/box_sdk_gen/schemas/ai_llm_endpoint_params_ibm.py @@ -0,0 +1,47 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiLlmEndpointParamsIbmTypeField(str, Enum): + IBM_PARAMS = 'ibm_params' + + +class AiLlmEndpointParamsIbm(BaseObject): + _discriminator = 'type', {'ibm_params'} + + def __init__( + self, + *, + type: AiLlmEndpointParamsIbmTypeField = AiLlmEndpointParamsIbmTypeField.IBM_PARAMS, + temperature: Optional[float] = None, + top_p: Optional[float] = None, + top_k: Optional[float] = None, + **kwargs + ): + """ + :param type: The type of the AI LLM endpoint params object for IBM. + This parameter is **required**., defaults to AiLlmEndpointParamsIbmTypeField.IBM_PARAMS + :type type: AiLlmEndpointParamsIbmTypeField, optional + :param temperature: What sampling temperature to use, between 0 and 1. Higher values like 0.8 will make the output more random, + while lower values like 0.2 will make it more focused and deterministic. + We generally recommend altering this or `top_p` but not both., defaults to None + :type temperature: Optional[float], optional + :param top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results + of the tokens with `top_p` probability mass. So 0.1 means only the tokens comprising the top 10% probability + mass are considered. We generally recommend altering this or temperature but not both., defaults to None + :type top_p: Optional[float], optional + :param top_k: `Top-K` changes how the model selects tokens for output. A low `top-K` means the next selected token is + the most probable among all tokens in the model's vocabulary (also called greedy decoding), + while a high `top-K` means that the next token is selected from among the three most probable tokens by using temperature., defaults to None + :type top_k: Optional[float], optional + """ + super().__init__(**kwargs) + self.type = type + self.temperature = temperature + self.top_p = top_p + self.top_k = top_k diff --git a/box_sdk_gen/schemas/ai_llm_endpoint_params_open_ai.py b/box_sdk_gen/schemas/ai_llm_endpoint_params_open_ai.py new file mode 100644 index 000000000..77264f885 --- /dev/null +++ b/box_sdk_gen/schemas/ai_llm_endpoint_params_open_ai.py @@ -0,0 +1,54 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiLlmEndpointParamsOpenAiTypeField(str, Enum): + OPENAI_PARAMS = 'openai_params' + + +class AiLlmEndpointParamsOpenAi(BaseObject): + _discriminator = 'type', {'openai_params'} + + def __init__( + self, + *, + type: AiLlmEndpointParamsOpenAiTypeField = AiLlmEndpointParamsOpenAiTypeField.OPENAI_PARAMS, + temperature: Optional[float] = None, + top_p: Optional[float] = None, + frequency_penalty: Optional[float] = None, + presence_penalty: Optional[float] = None, + stop: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of the AI LLM endpoint params object for OpenAI. + This parameter is **required**., defaults to AiLlmEndpointParamsOpenAiTypeField.OPENAI_PARAMS + :type type: AiLlmEndpointParamsOpenAiTypeField, optional + :param temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, + while lower values like 0.2 will make it more focused and deterministic. + We generally recommend altering this or `top_p` but not both., defaults to None + :type temperature: Optional[float], optional + :param top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results + of the tokens with `top_p` probability mass. So 0.1 means only the tokens comprising the top 10% probability + mass are considered. We generally recommend altering this or temperature but not both., defaults to None + :type top_p: Optional[float], optional + :param frequency_penalty: A number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the + text so far, decreasing the model's likelihood to repeat the same line verbatim., defaults to None + :type frequency_penalty: Optional[float], optional + :param presence_penalty: A number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics., defaults to None + :type presence_penalty: Optional[float], optional + :param stop: Up to 4 sequences where the API will stop generating further tokens., defaults to None + :type stop: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.temperature = temperature + self.top_p = top_p + self.frequency_penalty = frequency_penalty + self.presence_penalty = presence_penalty + self.stop = stop diff --git a/box_sdk_gen/schemas/ai_multiple_agent_response.py b/box_sdk_gen/schemas/ai_multiple_agent_response.py new file mode 100644 index 000000000..fad0593d5 --- /dev/null +++ b/box_sdk_gen/schemas/ai_multiple_agent_response.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_single_agent_response_full import AiSingleAgentResponseFull + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiMultipleAgentResponse(BaseObject): + def __init__( + self, + entries: List[AiSingleAgentResponseFull], + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: The list of AI Agents. + :type entries: List[AiSingleAgentResponseFull] + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker diff --git a/box_sdk_gen/schemas/ai_response.py b/box_sdk_gen/schemas/ai_response.py new file mode 100644 index 000000000..ff59efbc3 --- /dev/null +++ b/box_sdk_gen/schemas/ai_response.py @@ -0,0 +1,34 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_agent_info import AiAgentInfo + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class AiResponse(BaseObject): + def __init__( + self, + answer: str, + created_at: DateTime, + *, + completion_reason: Optional[str] = None, + ai_agent_info: Optional[AiAgentInfo] = None, + **kwargs + ): + """ + :param answer: The answer provided by the LLM. + :type answer: str + :param created_at: The ISO date formatted timestamp of when the answer to the prompt was created. + :type created_at: DateTime + :param completion_reason: The reason the response finishes., defaults to None + :type completion_reason: Optional[str], optional + """ + super().__init__(**kwargs) + self.answer = answer + self.created_at = created_at + self.completion_reason = completion_reason + self.ai_agent_info = ai_agent_info diff --git a/box_sdk_gen/schemas/ai_response_full.py b/box_sdk_gen/schemas/ai_response_full.py new file mode 100644 index 000000000..7c9a1c89f --- /dev/null +++ b/box_sdk_gen/schemas/ai_response_full.py @@ -0,0 +1,44 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.ai_agent_info import AiAgentInfo + +from box_sdk_gen.schemas.ai_response import AiResponse + +from box_sdk_gen.schemas.ai_citation import AiCitation + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiResponseFull(AiResponse): + def __init__( + self, + answer: str, + created_at: DateTime, + *, + citations: Optional[List[AiCitation]] = None, + completion_reason: Optional[str] = None, + ai_agent_info: Optional[AiAgentInfo] = None, + **kwargs + ): + """ + :param answer: The answer provided by the LLM. + :type answer: str + :param created_at: The ISO date formatted timestamp of when the answer to the prompt was created. + :type created_at: DateTime + :param citations: The citations of the LLM's answer reference., defaults to None + :type citations: Optional[List[AiCitation]], optional + :param completion_reason: The reason the response finishes., defaults to None + :type completion_reason: Optional[str], optional + """ + super().__init__( + answer=answer, + created_at=created_at, + completion_reason=completion_reason, + ai_agent_info=ai_agent_info, + **kwargs + ) + self.citations = citations diff --git a/box_sdk_gen/schemas/ai_single_agent_response.py b/box_sdk_gen/schemas/ai_single_agent_response.py new file mode 100644 index 000000000..e7d778b18 --- /dev/null +++ b/box_sdk_gen/schemas/ai_single_agent_response.py @@ -0,0 +1,78 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.schemas.ai_agent_allowed_entity import AiAgentAllowedEntity + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class AiSingleAgentResponseTypeField(str, Enum): + AI_AGENT = 'ai_agent' + + +class AiSingleAgentResponse(BaseObject): + _discriminator = 'type', {'ai_agent'} + + def __init__( + self, + id: str, + origin: str, + name: str, + access_state: str, + *, + type: Optional[AiSingleAgentResponseTypeField] = None, + created_by: Optional[UserBase] = None, + created_at: Optional[DateTime] = None, + modified_by: Optional[UserBase] = None, + modified_at: Optional[DateTime] = None, + icon_reference: Optional[str] = None, + allowed_entities: Optional[List[AiAgentAllowedEntity]] = None, + **kwargs + ): + """ + :param id: The unique identifier of the AI Agent. + :type id: str + :param origin: The provider of the AI Agent. + :type origin: str + :param name: The name of the AI Agent. + :type name: str + :param access_state: The state of the AI Agent. Possible values are: `enabled`, `disabled`, and `enabled_for_selected_users`. + :type access_state: str + :param type: The type of agent used to handle queries., defaults to None + :type type: Optional[AiSingleAgentResponseTypeField], optional + :param created_by: The user who created this agent., defaults to None + :type created_by: Optional[UserBase], optional + :param created_at: The ISO date-time formatted timestamp of when this AI agent was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_by: The user who most recently modified this agent., defaults to None + :type modified_by: Optional[UserBase], optional + :param modified_at: The ISO date-time formatted timestamp of when this AI agent was recently modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param icon_reference: The icon reference of the AI Agent., defaults to None + :type icon_reference: Optional[str], optional + :param allowed_entities: List of allowed users or groups., defaults to None + :type allowed_entities: Optional[List[AiAgentAllowedEntity]], optional + """ + super().__init__(**kwargs) + self.id = id + self.origin = origin + self.name = name + self.access_state = access_state + self.type = type + self.created_by = created_by + self.created_at = created_at + self.modified_by = modified_by + self.modified_at = modified_at + self.icon_reference = icon_reference + self.allowed_entities = allowed_entities diff --git a/box_sdk_gen/schemas/ai_single_agent_response_full.py b/box_sdk_gen/schemas/ai_single_agent_response_full.py new file mode 100644 index 000000000..1898fd5e7 --- /dev/null +++ b/box_sdk_gen/schemas/ai_single_agent_response_full.py @@ -0,0 +1,90 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.ai_single_agent_response import AiSingleAgentResponseTypeField + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.schemas.ai_agent_allowed_entity import AiAgentAllowedEntity + +from box_sdk_gen.schemas.ai_single_agent_response import AiSingleAgentResponse + +from box_sdk_gen.schemas.ai_studio_agent_ask_response import AiStudioAgentAskResponse + +from box_sdk_gen.schemas.ai_studio_agent_text_gen_response import ( + AiStudioAgentTextGenResponse, +) + +from box_sdk_gen.schemas.ai_studio_agent_extract_response import ( + AiStudioAgentExtractResponse, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiSingleAgentResponseFull(AiSingleAgentResponse): + def __init__( + self, + id: str, + origin: str, + name: str, + access_state: str, + *, + ask: Optional[AiStudioAgentAskResponse] = None, + text_gen: Optional[AiStudioAgentTextGenResponse] = None, + extract: Optional[AiStudioAgentExtractResponse] = None, + type: Optional[AiSingleAgentResponseTypeField] = None, + created_by: Optional[UserBase] = None, + created_at: Optional[DateTime] = None, + modified_by: Optional[UserBase] = None, + modified_at: Optional[DateTime] = None, + icon_reference: Optional[str] = None, + allowed_entities: Optional[List[AiAgentAllowedEntity]] = None, + **kwargs + ): + """ + :param id: The unique identifier of the AI Agent. + :type id: str + :param origin: The provider of the AI Agent. + :type origin: str + :param name: The name of the AI Agent. + :type name: str + :param access_state: The state of the AI Agent. Possible values are: `enabled`, `disabled`, and `enabled_for_selected_users`. + :type access_state: str + :param type: The type of agent used to handle queries., defaults to None + :type type: Optional[AiSingleAgentResponseTypeField], optional + :param created_by: The user who created this agent., defaults to None + :type created_by: Optional[UserBase], optional + :param created_at: The ISO date-time formatted timestamp of when this AI agent was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_by: The user who most recently modified this agent., defaults to None + :type modified_by: Optional[UserBase], optional + :param modified_at: The ISO date-time formatted timestamp of when this AI agent was recently modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param icon_reference: The icon reference of the AI Agent., defaults to None + :type icon_reference: Optional[str], optional + :param allowed_entities: List of allowed users or groups., defaults to None + :type allowed_entities: Optional[List[AiAgentAllowedEntity]], optional + """ + super().__init__( + id=id, + origin=origin, + name=name, + access_state=access_state, + type=type, + created_by=created_by, + created_at=created_at, + modified_by=modified_by, + modified_at=modified_at, + icon_reference=icon_reference, + allowed_entities=allowed_entities, + **kwargs + ) + self.ask = ask + self.text_gen = text_gen + self.extract = extract diff --git a/box_sdk_gen/schemas/ai_studio_agent_ask.py b/box_sdk_gen/schemas/ai_studio_agent_ask.py new file mode 100644 index 000000000..43f92b4d8 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_ask.py @@ -0,0 +1,70 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_studio_agent_long_text_tool import AiStudioAgentLongTextTool + +from box_sdk_gen.schemas.ai_studio_agent_basic_text_tool import ( + AiStudioAgentBasicTextTool, +) + +from box_sdk_gen.schemas.ai_studio_agent_spreadsheet_tool import ( + AiStudioAgentSpreadsheetTool, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentAskTypeField(str, Enum): + AI_AGENT_ASK = 'ai_agent_ask' + + +class AiStudioAgentAsk(BaseObject): + _discriminator = 'type', {'ai_agent_ask'} + + def __init__( + self, + access_state: str, + description: str, + *, + type: AiStudioAgentAskTypeField = AiStudioAgentAskTypeField.AI_AGENT_ASK, + custom_instructions: Optional[str] = None, + suggested_questions: Optional[List[str]] = None, + long_text: Optional[AiStudioAgentLongTextTool] = None, + basic_text: Optional[AiStudioAgentBasicTextTool] = None, + basic_image: Optional[AiStudioAgentBasicTextTool] = None, + spreadsheet: Optional[AiStudioAgentSpreadsheetTool] = None, + long_text_multi: Optional[AiStudioAgentLongTextTool] = None, + basic_text_multi: Optional[AiStudioAgentBasicTextTool] = None, + basic_image_multi: Optional[AiStudioAgentBasicTextTool] = None, + **kwargs + ): + """ + :param access_state: The state of the AI Agent capability. Possible values are: `enabled` and `disabled`. + :type access_state: str + :param description: The description of the AI agent. + :type description: str + :param type: The type of AI agent used to handle queries., defaults to AiStudioAgentAskTypeField.AI_AGENT_ASK + :type type: AiStudioAgentAskTypeField, optional + :param custom_instructions: Custom instructions for the AI agent., defaults to None + :type custom_instructions: Optional[str], optional + :param suggested_questions: Suggested questions for the AI agent. If null, suggested question will be generated. If empty, no suggested questions will be displayed., defaults to None + :type suggested_questions: Optional[List[str]], optional + """ + super().__init__(**kwargs) + self.access_state = access_state + self.description = description + self.type = type + self.custom_instructions = custom_instructions + self.suggested_questions = suggested_questions + self.long_text = long_text + self.basic_text = basic_text + self.basic_image = basic_image + self.spreadsheet = spreadsheet + self.long_text_multi = long_text_multi + self.basic_text_multi = basic_text_multi + self.basic_image_multi = basic_image_multi diff --git a/box_sdk_gen/schemas/ai_studio_agent_ask_response.py b/box_sdk_gen/schemas/ai_studio_agent_ask_response.py new file mode 100644 index 000000000..89d100337 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_ask_response.py @@ -0,0 +1,72 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_studio_agent_long_text_tool_response import ( + AiStudioAgentLongTextToolResponse, +) + +from box_sdk_gen.schemas.ai_studio_agent_basic_text_tool_response import ( + AiStudioAgentBasicTextToolResponse, +) + +from box_sdk_gen.schemas.ai_studio_agent_spreadsheet_tool_response import ( + AiStudioAgentSpreadsheetToolResponse, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentAskResponseTypeField(str, Enum): + AI_AGENT_ASK = 'ai_agent_ask' + + +class AiStudioAgentAskResponse(BaseObject): + _discriminator = 'type', {'ai_agent_ask'} + + def __init__( + self, + access_state: str, + description: str, + *, + type: AiStudioAgentAskResponseTypeField = AiStudioAgentAskResponseTypeField.AI_AGENT_ASK, + custom_instructions: Optional[str] = None, + suggested_questions: Optional[List[str]] = None, + long_text: Optional[AiStudioAgentLongTextToolResponse] = None, + basic_text: Optional[AiStudioAgentBasicTextToolResponse] = None, + basic_image: Optional[AiStudioAgentBasicTextToolResponse] = None, + spreadsheet: Optional[AiStudioAgentSpreadsheetToolResponse] = None, + long_text_multi: Optional[AiStudioAgentLongTextToolResponse] = None, + basic_text_multi: Optional[AiStudioAgentBasicTextToolResponse] = None, + basic_image_multi: Optional[AiStudioAgentBasicTextToolResponse] = None, + **kwargs + ): + """ + :param access_state: The state of the AI Agent capability. Possible values are: `enabled` and `disabled`. + :type access_state: str + :param description: The description of the AI agent. + :type description: str + :param type: The type of AI agent used to ask questions., defaults to AiStudioAgentAskResponseTypeField.AI_AGENT_ASK + :type type: AiStudioAgentAskResponseTypeField, optional + :param custom_instructions: Custom instructions for the AI agent., defaults to None + :type custom_instructions: Optional[str], optional + :param suggested_questions: Suggested questions for the AI agent. If null, suggested question will be generated. If empty, no suggested questions will be displayed., defaults to None + :type suggested_questions: Optional[List[str]], optional + """ + super().__init__(**kwargs) + self.access_state = access_state + self.description = description + self.type = type + self.custom_instructions = custom_instructions + self.suggested_questions = suggested_questions + self.long_text = long_text + self.basic_text = basic_text + self.basic_image = basic_image + self.spreadsheet = spreadsheet + self.long_text_multi = long_text_multi + self.basic_text_multi = basic_text_multi + self.basic_image_multi = basic_image_multi diff --git a/box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool.py b/box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool.py new file mode 100644 index 000000000..b3ed99e08 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool.py @@ -0,0 +1,75 @@ +from typing import Optional + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_text_gen import ( + AiAgentBasicTextToolTextGen, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool_text_gen import ( + AiAgentLongTextToolTextGenEmbeddingsField, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool_text_gen import ( + AiAgentLongTextToolTextGen, +) + +from box_sdk_gen.schemas.ai_agent_basic_gen_tool import AiAgentBasicGenTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentBasicGenTool(AiAgentBasicGenTool): + def __init__( + self, + *, + is_custom_instructions_included: Optional[bool] = None, + content_template: Optional[str] = None, + embeddings: Optional[AiAgentLongTextToolTextGenEmbeddingsField] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param is_custom_instructions_included: True if system message contains custom instructions placeholder, false otherwise., defaults to None + :type is_custom_instructions_included: Optional[bool], optional + :param content_template: How the content should be included in a request to the LLM. + Input for `{content}` is optional, depending on the use., defaults to None + :type content_template: Optional[str], optional + :param system_message: System messages aim at helping the LLM understand its role and what it is supposed to do. + The input for `{current_date}` is optional, depending on the use., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + + When using the `prompt_template` parameter, you **must include** input for `{user_question}`. + Inputs for `{current_date}` and `{content}` are optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + content_template=content_template, + embeddings=embeddings, + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.is_custom_instructions_included = is_custom_instructions_included diff --git a/box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool_response.py b/box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool_response.py new file mode 100644 index 000000000..4e418497c --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_basic_gen_tool_response.py @@ -0,0 +1,83 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_text_gen import ( + AiAgentBasicTextToolTextGen, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool_text_gen import ( + AiAgentLongTextToolTextGenEmbeddingsField, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool_text_gen import ( + AiAgentLongTextToolTextGen, +) + +from box_sdk_gen.schemas.ai_agent_basic_gen_tool import AiAgentBasicGenTool + +from box_sdk_gen.schemas.ai_studio_agent_basic_gen_tool import AiStudioAgentBasicGenTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentBasicGenToolResponse(AiStudioAgentBasicGenTool): + def __init__( + self, + *, + warnings: Optional[List[str]] = None, + is_custom_instructions_included: Optional[bool] = None, + content_template: Optional[str] = None, + embeddings: Optional[AiAgentLongTextToolTextGenEmbeddingsField] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param warnings: Warnings concerning tool., defaults to None + :type warnings: Optional[List[str]], optional + :param is_custom_instructions_included: True if system message contains custom instructions placeholder, false otherwise., defaults to None + :type is_custom_instructions_included: Optional[bool], optional + :param content_template: How the content should be included in a request to the LLM. + Input for `{content}` is optional, depending on the use., defaults to None + :type content_template: Optional[str], optional + :param system_message: System messages aim at helping the LLM understand its role and what it is supposed to do. + The input for `{current_date}` is optional, depending on the use., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + + When using the `prompt_template` parameter, you **must include** input for `{user_question}`. + Inputs for `{current_date}` and `{content}` are optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + is_custom_instructions_included=is_custom_instructions_included, + content_template=content_template, + embeddings=embeddings, + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.warnings = warnings diff --git a/box_sdk_gen/schemas/ai_studio_agent_basic_text_tool.py b/box_sdk_gen/schemas/ai_studio_agent_basic_text_tool.py new file mode 100644 index 000000000..bb1435fe5 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_basic_text_tool.py @@ -0,0 +1,54 @@ +from typing import Optional + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentBasicTextTool(AiAgentBasicTextTool): + def __init__( + self, + *, + is_custom_instructions_included: Optional[bool] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param is_custom_instructions_included: True if system message contains custom instructions placeholder, false otherwise., defaults to None + :type is_custom_instructions_included: Optional[bool], optional + :param system_message: System messages try to help the LLM "understand" its role and what it is supposed to do., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + When passing `prompt_template` parameters, you **must include** inputs for `{user_question}` and `{content}`. + `{current_date}` is optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.is_custom_instructions_included = is_custom_instructions_included diff --git a/box_sdk_gen/schemas/ai_studio_agent_basic_text_tool_response.py b/box_sdk_gen/schemas/ai_studio_agent_basic_text_tool_response.py new file mode 100644 index 000000000..47b4eaf25 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_basic_text_tool_response.py @@ -0,0 +1,64 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.schemas.ai_studio_agent_basic_text_tool import ( + AiStudioAgentBasicTextTool, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentBasicTextToolResponse(AiStudioAgentBasicTextTool): + def __init__( + self, + *, + warnings: Optional[List[str]] = None, + is_custom_instructions_included: Optional[bool] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param warnings: Warnings concerning tool., defaults to None + :type warnings: Optional[List[str]], optional + :param is_custom_instructions_included: True if system message contains custom instructions placeholder, false otherwise., defaults to None + :type is_custom_instructions_included: Optional[bool], optional + :param system_message: System messages try to help the LLM "understand" its role and what it is supposed to do., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + When passing `prompt_template` parameters, you **must include** inputs for `{user_question}` and `{content}`. + `{current_date}` is optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + is_custom_instructions_included=is_custom_instructions_included, + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.warnings = warnings diff --git a/box_sdk_gen/schemas/ai_studio_agent_extract.py b/box_sdk_gen/schemas/ai_studio_agent_extract.py new file mode 100644 index 000000000..1d090d2da --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_extract.py @@ -0,0 +1,52 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_studio_agent_long_text_tool import AiStudioAgentLongTextTool + +from box_sdk_gen.schemas.ai_studio_agent_basic_text_tool import ( + AiStudioAgentBasicTextTool, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentExtractTypeField(str, Enum): + AI_AGENT_EXTRACT = 'ai_agent_extract' + + +class AiStudioAgentExtract(BaseObject): + _discriminator = 'type', {'ai_agent_extract'} + + def __init__( + self, + access_state: str, + description: str, + *, + type: AiStudioAgentExtractTypeField = AiStudioAgentExtractTypeField.AI_AGENT_EXTRACT, + custom_instructions: Optional[str] = None, + long_text: Optional[AiStudioAgentLongTextTool] = None, + basic_text: Optional[AiStudioAgentBasicTextTool] = None, + basic_image: Optional[AiStudioAgentBasicTextTool] = None, + **kwargs + ): + """ + :param access_state: The state of the AI Agent capability. Possible values are: `enabled` and `disabled`. + :type access_state: str + :param description: The description of the AI agent. + :type description: str + :param type: The type of AI agent to be used for metadata extraction., defaults to AiStudioAgentExtractTypeField.AI_AGENT_EXTRACT + :type type: AiStudioAgentExtractTypeField, optional + :param custom_instructions: Custom instructions for the AI agent., defaults to None + :type custom_instructions: Optional[str], optional + """ + super().__init__(**kwargs) + self.access_state = access_state + self.description = description + self.type = type + self.custom_instructions = custom_instructions + self.long_text = long_text + self.basic_text = basic_text + self.basic_image = basic_image diff --git a/box_sdk_gen/schemas/ai_studio_agent_extract_response.py b/box_sdk_gen/schemas/ai_studio_agent_extract_response.py new file mode 100644 index 000000000..81ee75993 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_extract_response.py @@ -0,0 +1,54 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_studio_agent_long_text_tool_response import ( + AiStudioAgentLongTextToolResponse, +) + +from box_sdk_gen.schemas.ai_studio_agent_basic_text_tool_response import ( + AiStudioAgentBasicTextToolResponse, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentExtractResponseTypeField(str, Enum): + AI_AGENT_EXTRACT = 'ai_agent_extract' + + +class AiStudioAgentExtractResponse(BaseObject): + _discriminator = 'type', {'ai_agent_extract'} + + def __init__( + self, + access_state: str, + description: str, + *, + type: AiStudioAgentExtractResponseTypeField = AiStudioAgentExtractResponseTypeField.AI_AGENT_EXTRACT, + custom_instructions: Optional[str] = None, + long_text: Optional[AiStudioAgentLongTextToolResponse] = None, + basic_text: Optional[AiStudioAgentBasicTextToolResponse] = None, + basic_image: Optional[AiStudioAgentBasicTextToolResponse] = None, + **kwargs + ): + """ + :param access_state: The state of the AI Agent capability. Possible values are: `enabled` and `disabled`. + :type access_state: str + :param description: The description of the AI agent. + :type description: str + :param type: The type of AI agent to be used for metadata extraction., defaults to AiStudioAgentExtractResponseTypeField.AI_AGENT_EXTRACT + :type type: AiStudioAgentExtractResponseTypeField, optional + :param custom_instructions: Custom instructions for the AI agent., defaults to None + :type custom_instructions: Optional[str], optional + """ + super().__init__(**kwargs) + self.access_state = access_state + self.description = description + self.type = type + self.custom_instructions = custom_instructions + self.long_text = long_text + self.basic_text = basic_text + self.basic_image = basic_image diff --git a/box_sdk_gen/schemas/ai_studio_agent_long_text_tool.py b/box_sdk_gen/schemas/ai_studio_agent_long_text_tool.py new file mode 100644 index 000000000..5419f0b12 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_long_text_tool.py @@ -0,0 +1,62 @@ +from typing import Optional + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.schemas.ai_agent_long_text_tool import ( + AiAgentLongTextToolEmbeddingsField, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool import AiAgentLongTextTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentLongTextTool(AiAgentLongTextTool): + def __init__( + self, + *, + is_custom_instructions_included: Optional[bool] = None, + embeddings: Optional[AiAgentLongTextToolEmbeddingsField] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param is_custom_instructions_included: True if system message contains custom instructions placeholder, false otherwise., defaults to None + :type is_custom_instructions_included: Optional[bool], optional + :param system_message: System messages try to help the LLM "understand" its role and what it is supposed to do., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + When passing `prompt_template` parameters, you **must include** inputs for `{user_question}` and `{content}`. + `{current_date}` is optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + embeddings=embeddings, + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.is_custom_instructions_included = is_custom_instructions_included diff --git a/box_sdk_gen/schemas/ai_studio_agent_long_text_tool_response.py b/box_sdk_gen/schemas/ai_studio_agent_long_text_tool_response.py new file mode 100644 index 000000000..b521b3751 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_long_text_tool_response.py @@ -0,0 +1,70 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_basic_text_tool_base import AiAgentBasicTextToolBase + +from box_sdk_gen.schemas.ai_agent_basic_text_tool import AiAgentBasicTextTool + +from box_sdk_gen.schemas.ai_agent_long_text_tool import ( + AiAgentLongTextToolEmbeddingsField, +) + +from box_sdk_gen.schemas.ai_agent_long_text_tool import AiAgentLongTextTool + +from box_sdk_gen.schemas.ai_studio_agent_long_text_tool import AiStudioAgentLongTextTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentLongTextToolResponse(AiStudioAgentLongTextTool): + def __init__( + self, + *, + warnings: Optional[List[str]] = None, + is_custom_instructions_included: Optional[bool] = None, + embeddings: Optional[AiAgentLongTextToolEmbeddingsField] = None, + system_message: Optional[str] = None, + prompt_template: Optional[str] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param warnings: Warnings concerning tool., defaults to None + :type warnings: Optional[List[str]], optional + :param is_custom_instructions_included: True if system message contains custom instructions placeholder, false otherwise., defaults to None + :type is_custom_instructions_included: Optional[bool], optional + :param system_message: System messages try to help the LLM "understand" its role and what it is supposed to do., defaults to None + :type system_message: Optional[str], optional + :param prompt_template: The prompt template contains contextual information of the request and the user prompt. + When passing `prompt_template` parameters, you **must include** inputs for `{user_question}` and `{content}`. + `{current_date}` is optional, depending on the use., defaults to None + :type prompt_template: Optional[str], optional + :param model: The model used for the AI agent for basic text. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + is_custom_instructions_included=is_custom_instructions_included, + embeddings=embeddings, + system_message=system_message, + prompt_template=prompt_template, + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.warnings = warnings diff --git a/box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool.py b/box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool.py new file mode 100644 index 000000000..9d207998f --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool.py @@ -0,0 +1,38 @@ +from typing import Optional + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_spreadsheet_tool import AiAgentSpreadsheetTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentSpreadsheetTool(AiAgentSpreadsheetTool): + def __init__( + self, + *, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param model: The model used for the AI agent for spreadsheets. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) diff --git a/box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool_response.py b/box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool_response.py new file mode 100644 index 000000000..bf6323140 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_spreadsheet_tool_response.py @@ -0,0 +1,48 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.ai_llm_endpoint_params_open_ai import AiLlmEndpointParamsOpenAi + +from box_sdk_gen.schemas.ai_llm_endpoint_params_google import AiLlmEndpointParamsGoogle + +from box_sdk_gen.schemas.ai_llm_endpoint_params_aws import AiLlmEndpointParamsAws + +from box_sdk_gen.schemas.ai_llm_endpoint_params_ibm import AiLlmEndpointParamsIbm + +from box_sdk_gen.schemas.ai_llm_endpoint_params import AiLlmEndpointParams + +from box_sdk_gen.schemas.ai_agent_spreadsheet_tool import AiAgentSpreadsheetTool + +from box_sdk_gen.schemas.ai_studio_agent_spreadsheet_tool import ( + AiStudioAgentSpreadsheetTool, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentSpreadsheetToolResponse(AiStudioAgentSpreadsheetTool): + def __init__( + self, + *, + warnings: Optional[List[str]] = None, + model: Optional[str] = None, + num_tokens_for_completion: Optional[int] = None, + llm_endpoint_params: Optional[AiLlmEndpointParams] = None, + **kwargs + ): + """ + :param warnings: Warnings concerning tool., defaults to None + :type warnings: Optional[List[str]], optional + :param model: The model used for the AI agent for spreadsheets. For specific model values, see the [available models list](g://box-ai/supported-models)., defaults to None + :type model: Optional[str], optional + :param num_tokens_for_completion: The number of tokens for completion., defaults to None + :type num_tokens_for_completion: Optional[int], optional + """ + super().__init__( + model=model, + num_tokens_for_completion=num_tokens_for_completion, + llm_endpoint_params=llm_endpoint_params, + **kwargs + ) + self.warnings = warnings diff --git a/box_sdk_gen/schemas/ai_studio_agent_text_gen.py b/box_sdk_gen/schemas/ai_studio_agent_text_gen.py new file mode 100644 index 000000000..c461a3761 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_text_gen.py @@ -0,0 +1,50 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_studio_agent_basic_gen_tool import AiStudioAgentBasicGenTool + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentTextGenTypeField(str, Enum): + AI_AGENT_TEXT_GEN = 'ai_agent_text_gen' + + +class AiStudioAgentTextGen(BaseObject): + _discriminator = 'type', {'ai_agent_text_gen'} + + def __init__( + self, + access_state: str, + description: str, + *, + type: AiStudioAgentTextGenTypeField = AiStudioAgentTextGenTypeField.AI_AGENT_TEXT_GEN, + custom_instructions: Optional[str] = None, + suggested_questions: Optional[List[str]] = None, + basic_gen: Optional[AiStudioAgentBasicGenTool] = None, + **kwargs + ): + """ + :param access_state: The state of the AI Agent capability. Possible values are: `enabled` and `disabled`. + :type access_state: str + :param description: The description of the AI agent. + :type description: str + :param type: The type of AI agent used for generating text., defaults to AiStudioAgentTextGenTypeField.AI_AGENT_TEXT_GEN + :type type: AiStudioAgentTextGenTypeField, optional + :param custom_instructions: Custom instructions for the AI agent., defaults to None + :type custom_instructions: Optional[str], optional + :param suggested_questions: Suggested questions for the AI agent. If null, suggested question will be generated. If empty, no suggested questions will be displayed., defaults to None + :type suggested_questions: Optional[List[str]], optional + """ + super().__init__(**kwargs) + self.access_state = access_state + self.description = description + self.type = type + self.custom_instructions = custom_instructions + self.suggested_questions = suggested_questions + self.basic_gen = basic_gen diff --git a/box_sdk_gen/schemas/ai_studio_agent_text_gen_response.py b/box_sdk_gen/schemas/ai_studio_agent_text_gen_response.py new file mode 100644 index 000000000..fdef65767 --- /dev/null +++ b/box_sdk_gen/schemas/ai_studio_agent_text_gen_response.py @@ -0,0 +1,52 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.ai_studio_agent_basic_gen_tool_response import ( + AiStudioAgentBasicGenToolResponse, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiStudioAgentTextGenResponseTypeField(str, Enum): + AI_AGENT_TEXT_GEN = 'ai_agent_text_gen' + + +class AiStudioAgentTextGenResponse(BaseObject): + _discriminator = 'type', {'ai_agent_text_gen'} + + def __init__( + self, + access_state: str, + description: str, + *, + type: AiStudioAgentTextGenResponseTypeField = AiStudioAgentTextGenResponseTypeField.AI_AGENT_TEXT_GEN, + custom_instructions: Optional[str] = None, + suggested_questions: Optional[List[str]] = None, + basic_gen: Optional[AiStudioAgentBasicGenToolResponse] = None, + **kwargs + ): + """ + :param access_state: The state of the AI Agent capability. Possible values are: `enabled` and `disabled`. + :type access_state: str + :param description: The description of the AI agent. + :type description: str + :param type: The type of AI agent used for generating text., defaults to AiStudioAgentTextGenResponseTypeField.AI_AGENT_TEXT_GEN + :type type: AiStudioAgentTextGenResponseTypeField, optional + :param custom_instructions: Custom instructions for the AI agent., defaults to None + :type custom_instructions: Optional[str], optional + :param suggested_questions: Suggested questions for the AI agent. If null, suggested question will be generated. If empty, no suggested questions will be displayed., defaults to None + :type suggested_questions: Optional[List[str]], optional + """ + super().__init__(**kwargs) + self.access_state = access_state + self.description = description + self.type = type + self.custom_instructions = custom_instructions + self.suggested_questions = suggested_questions + self.basic_gen = basic_gen diff --git a/box_sdk_gen/schemas/ai_text_gen.py b/box_sdk_gen/schemas/ai_text_gen.py new file mode 100644 index 000000000..a994b793a --- /dev/null +++ b/box_sdk_gen/schemas/ai_text_gen.py @@ -0,0 +1,75 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_text_gen import AiAgentTextGen + +from box_sdk_gen.schemas.ai_dialogue_history import AiDialogueHistory + +from box_sdk_gen.schemas.ai_text_gen_agent import AiTextGenAgent + +from box_sdk_gen.box.errors import BoxSDKError + + +class AiTextGenItemsTypeField(str, Enum): + FILE = 'file' + + +class AiTextGenItemsField(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + id: str, + *, + type: AiTextGenItemsTypeField = AiTextGenItemsTypeField.FILE, + content: Optional[str] = None, + **kwargs + ): + """ + :param id: The ID of the item. + :type id: str + :param type: The type of the item., defaults to AiTextGenItemsTypeField.FILE + :type type: AiTextGenItemsTypeField, optional + :param content: The content to use as context for generating new text or editing existing text., defaults to None + :type content: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.content = content + + +class AiTextGen(BaseObject): + def __init__( + self, + prompt: str, + items: List[AiTextGenItemsField], + *, + dialogue_history: Optional[List[AiDialogueHistory]] = None, + ai_agent: Optional[AiTextGenAgent] = None, + **kwargs + ): + """ + :param prompt: The prompt provided by the client to be answered by the LLM. The prompt's length is limited to 10000 characters. + :type prompt: str + :param items: The items to be processed by the LLM, often files. + The array can include **exactly one** element. + + **Note**: Box AI handles documents with text representations up to 1MB in size. + If the file size exceeds 1MB, the first 1MB of text representation will be processed. + :type items: List[AiTextGenItemsField] + :param dialogue_history: The history of prompts and answers previously passed to the LLM. This parameter provides the additional context to the LLM when generating the response., defaults to None + :type dialogue_history: Optional[List[AiDialogueHistory]], optional + """ + super().__init__(**kwargs) + self.prompt = prompt + self.items = items + self.dialogue_history = dialogue_history + self.ai_agent = ai_agent diff --git a/box_sdk_gen/schemas/ai_text_gen_agent.py b/box_sdk_gen/schemas/ai_text_gen_agent.py new file mode 100644 index 000000000..1b4c82dae --- /dev/null +++ b/box_sdk_gen/schemas/ai_text_gen_agent.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.ai_agent_reference import AiAgentReference + +from box_sdk_gen.schemas.ai_agent_text_gen import AiAgentTextGen + +from box_sdk_gen.box.errors import BoxSDKError + +AiTextGenAgent = Union[AiAgentReference, AiAgentTextGen] diff --git a/box_sdk_gen/schemas/app_item.py b/box_sdk_gen/schemas/app_item.py new file mode 100644 index 000000000..d218c94e7 --- /dev/null +++ b/box_sdk_gen/schemas/app_item.py @@ -0,0 +1,34 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class AppItemTypeField(str, Enum): + APP_ITEM = 'app_item' + + +class AppItem(BaseObject): + _discriminator = 'type', {'app_item'} + + def __init__( + self, + id: str, + application_type: str, + *, + type: AppItemTypeField = AppItemTypeField.APP_ITEM, + **kwargs + ): + """ + :param id: The unique identifier for this app item. + :type id: str + :param application_type: The type of the app that owns this app item. + :type application_type: str + :param type: The value will always be `app_item`., defaults to AppItemTypeField.APP_ITEM + :type type: AppItemTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.application_type = application_type + self.type = type diff --git a/box_sdk_gen/schemas/app_item_associated_item.py b/box_sdk_gen/schemas/app_item_associated_item.py new file mode 100644 index 000000000..2b384c72b --- /dev/null +++ b/box_sdk_gen/schemas/app_item_associated_item.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.folder_base import FolderBase + +from box_sdk_gen.schemas.web_link_base import WebLinkBase + +from box_sdk_gen.box.errors import BoxSDKError + +AppItemAssociatedItem = Union[FileBase, FolderBase, WebLinkBase] diff --git a/box_sdk_gen/schemas/app_item_association.py b/box_sdk_gen/schemas/app_item_association.py new file mode 100644 index 000000000..3b615a190 --- /dev/null +++ b/box_sdk_gen/schemas/app_item_association.py @@ -0,0 +1,44 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.folder_base import FolderBase + +from box_sdk_gen.schemas.web_link_base import WebLinkBase + +from box_sdk_gen.schemas.app_item import AppItem + +from box_sdk_gen.schemas.app_item_associated_item import AppItemAssociatedItem + +from box_sdk_gen.box.errors import BoxSDKError + + +class AppItemAssociationTypeField(str, Enum): + APP_ITEM_ASSOCIATION = 'app_item_association' + + +class AppItemAssociation(BaseObject): + _discriminator = 'type', {'app_item_association'} + + def __init__( + self, + id: str, + app_item: AppItem, + item: AppItemAssociatedItem, + *, + type: AppItemAssociationTypeField = AppItemAssociationTypeField.APP_ITEM_ASSOCIATION, + **kwargs + ): + """ + :param id: The unique identifier for this app item association. + :type id: str + :param type: The value will always be `app_item_association`., defaults to AppItemAssociationTypeField.APP_ITEM_ASSOCIATION + :type type: AppItemAssociationTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.app_item = app_item + self.item = item + self.type = type diff --git a/box_sdk_gen/schemas/app_item_associations.py b/box_sdk_gen/schemas/app_item_associations.py new file mode 100644 index 000000000..4cb940a3b --- /dev/null +++ b/box_sdk_gen/schemas/app_item_associations.py @@ -0,0 +1,36 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.app_item_association import AppItemAssociation + +from box_sdk_gen.box.errors import BoxSDKError + + +class AppItemAssociations(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[AppItemAssociation]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/app_item_event_source.py b/box_sdk_gen/schemas/app_item_event_source.py new file mode 100644 index 000000000..640847d86 --- /dev/null +++ b/box_sdk_gen/schemas/app_item_event_source.py @@ -0,0 +1,44 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.group_mini import GroupMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class AppItemEventSourceTypeField(str, Enum): + APP_ITEM = 'app_item' + + +class AppItemEventSource(BaseObject): + _discriminator = 'type', {'app_item'} + + def __init__( + self, + id: str, + app_item_type: str, + *, + type: AppItemEventSourceTypeField = AppItemEventSourceTypeField.APP_ITEM, + user: Optional[UserMini] = None, + group: Optional[GroupMini] = None, + **kwargs + ): + """ + :param id: The id of the `AppItem`. + :type id: str + :param app_item_type: The type of the `AppItem`. + :type app_item_type: str + :param type: The type of the source that this event represents. Can only be `app_item`., defaults to AppItemEventSourceTypeField.APP_ITEM + :type type: AppItemEventSourceTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.app_item_type = app_item_type + self.type = type + self.user = user + self.group = group diff --git a/box_sdk_gen/schemas/classification.py b/box_sdk_gen/schemas/classification.py new file mode 100644 index 000000000..54feab5c4 --- /dev/null +++ b/box_sdk_gen/schemas/classification.py @@ -0,0 +1,88 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ClassificationTemplateField(str, Enum): + SECURITYCLASSIFICATION_6VMVOCHWUWO = 'securityClassification-6VMVochwUWo' + + +class Classification(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'box_security_classification_key': 'Box__Security__Classification__Key', + 'parent': '$parent', + 'template': '$template', + 'scope': '$scope', + 'version': '$version', + 'type': '$type', + 'type_version': '$typeVersion', + 'can_edit': '$canEdit', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'Box__Security__Classification__Key': 'box_security_classification_key', + '$parent': 'parent', + '$template': 'template', + '$scope': 'scope', + '$version': 'version', + '$type': 'type', + '$typeVersion': 'type_version', + '$canEdit': 'can_edit', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + box_security_classification_key: Optional[str] = None, + parent: Optional[str] = None, + template: Optional[ClassificationTemplateField] = None, + scope: Optional[str] = None, + version: Optional[int] = None, + type: Optional[str] = None, + type_version: Optional[float] = None, + can_edit: Optional[bool] = None, + **kwargs + ): + """ + :param box_security_classification_key: The name of the classification applied to the item., defaults to None + :type box_security_classification_key: Optional[str], optional + :param parent: The identifier of the item that this metadata instance + has been attached to. This combines the `type` and the `id` + of the parent in the form `{type}_{id}`., defaults to None + :type parent: Optional[str], optional + :param template: The value will always be `securityClassification-6VMVochwUWo`., defaults to None + :type template: Optional[ClassificationTemplateField], optional + :param scope: The scope of the enterprise that this classification has been + applied for. + + This will be in the format `enterprise_{enterprise_id}`., defaults to None + :type scope: Optional[str], optional + :param version: The version of the metadata instance. This version starts at 0 and + increases every time a classification is updated., defaults to None + :type version: Optional[int], optional + :param type: The unique ID of this classification instance. This will be include + the name of the classification template and a unique ID., defaults to None + :type type: Optional[str], optional + :param type_version: The version of the metadata template. This version starts at 0 and + increases every time the template is updated. This is mostly for internal + use., defaults to None + :type type_version: Optional[float], optional + :param can_edit: Whether an end user can change the classification., defaults to None + :type can_edit: Optional[bool], optional + """ + super().__init__(**kwargs) + self.box_security_classification_key = box_security_classification_key + self.parent = parent + self.template = template + self.scope = scope + self.version = version + self.type = type + self.type_version = type_version + self.can_edit = can_edit diff --git a/box_sdk_gen/schemas/classification_template.py b/box_sdk_gen/schemas/classification_template.py new file mode 100644 index 000000000..7bb03cb07 --- /dev/null +++ b/box_sdk_gen/schemas/classification_template.py @@ -0,0 +1,246 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + + +class ClassificationTemplateTypeField(str, Enum): + METADATA_TEMPLATE = 'metadata_template' + + +class ClassificationTemplateTemplateKeyField(str, Enum): + SECURITYCLASSIFICATION_6VMVOCHWUWO = 'securityClassification-6VMVochwUWo' + + +class ClassificationTemplateDisplayNameField(str, Enum): + CLASSIFICATION = 'Classification' + + +class ClassificationTemplateFieldsTypeField(str, Enum): + ENUM = 'enum' + + +class ClassificationTemplateFieldsKeyField(str, Enum): + BOX__SECURITY__CLASSIFICATION__KEY = 'Box__Security__Classification__Key' + + +class ClassificationTemplateFieldsDisplayNameField(str, Enum): + CLASSIFICATION = 'Classification' + + +class ClassificationTemplateFieldsOptionsStaticConfigClassificationField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'classification_definition': 'classificationDefinition', + 'color_id': 'colorID', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'classificationDefinition': 'classification_definition', + 'colorID': 'color_id', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + classification_definition: Optional[str] = None, + color_id: Optional[int] = None, + **kwargs + ): + """ + :param classification_definition: A longer description of the classification., defaults to None + :type classification_definition: Optional[str], optional + :param color_id: An internal Box identifier used to assign a color to + a classification label. + + Mapping between a `colorID` and a color may change + without notice. Currently, the color mappings are as + follows. + + * `0`: Yellow. + * `1`: Orange. + * `2`: Watermelon red. + * `3`: Purple rain. + * `4`: Light blue. + * `5`: Dark blue. + * `6`: Light green. + * `7`: Gray., defaults to None + :type color_id: Optional[int], optional + """ + super().__init__(**kwargs) + self.classification_definition = classification_definition + self.color_id = color_id + + +class ClassificationTemplateFieldsOptionsStaticConfigField(BaseObject): + def __init__( + self, + *, + classification: Optional[ + ClassificationTemplateFieldsOptionsStaticConfigClassificationField + ] = None, + **kwargs + ): + """ + :param classification: Additional information about the classification. + + This is not an exclusive list of properties, and + more object fields might be returned. These fields + are used for internal Box Shield and Box Governance + purposes and no additional value must be derived from + these fields., defaults to None + :type classification: Optional[ClassificationTemplateFieldsOptionsStaticConfigClassificationField], optional + """ + super().__init__(**kwargs) + self.classification = classification + + +class ClassificationTemplateFieldsOptionsField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'static_config': 'staticConfig', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'staticConfig': 'static_config', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + id: str, + key: str, + *, + static_config: Optional[ + ClassificationTemplateFieldsOptionsStaticConfigField + ] = None, + **kwargs + ): + """ + :param id: The unique ID of this classification. + :type id: str + :param key: The display name and key for this classification. + :type key: str + :param static_config: Additional information about the classification., defaults to None + :type static_config: Optional[ClassificationTemplateFieldsOptionsStaticConfigField], optional + """ + super().__init__(**kwargs) + self.id = id + self.key = key + self.static_config = static_config + + +class ClassificationTemplateFieldsField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'display_name': 'displayName', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'displayName': 'display_name', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'enum'} + + def __init__( + self, + id: str, + options: List[ClassificationTemplateFieldsOptionsField], + *, + type: ClassificationTemplateFieldsTypeField = ClassificationTemplateFieldsTypeField.ENUM, + key: ClassificationTemplateFieldsKeyField = ClassificationTemplateFieldsKeyField.BOX__SECURITY__CLASSIFICATION__KEY, + display_name: ClassificationTemplateFieldsDisplayNameField = ClassificationTemplateFieldsDisplayNameField.CLASSIFICATION, + hidden: Optional[bool] = None, + **kwargs + ): + """ + :param id: The unique ID of the field. + :type id: str + :param options: A list of classifications available in this enterprise. + :type options: List[ClassificationTemplateFieldsOptionsField] + :param type: The array item type., defaults to ClassificationTemplateFieldsTypeField.ENUM + :type type: ClassificationTemplateFieldsTypeField, optional + :param key: Defines classifications + available in the enterprise., defaults to ClassificationTemplateFieldsKeyField.BOX__SECURITY__CLASSIFICATION__KEY + :type key: ClassificationTemplateFieldsKeyField, optional + :param display_name: The value will always be `Classification`., defaults to ClassificationTemplateFieldsDisplayNameField.CLASSIFICATION + :type display_name: ClassificationTemplateFieldsDisplayNameField, optional + :param hidden: Classifications are always visible to web and mobile users., defaults to None + :type hidden: Optional[bool], optional + """ + super().__init__(**kwargs) + self.id = id + self.options = options + self.type = type + self.key = key + self.display_name = display_name + self.hidden = hidden + + +class ClassificationTemplate(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'template_key': 'templateKey', + 'display_name': 'displayName', + 'copy_instance_on_item_copy': 'copyInstanceOnItemCopy', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'templateKey': 'template_key', + 'displayName': 'display_name', + 'copyInstanceOnItemCopy': 'copy_instance_on_item_copy', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'metadata_template'} + + def __init__( + self, + id: str, + scope: str, + fields: List[ClassificationTemplateFieldsField], + *, + type: ClassificationTemplateTypeField = ClassificationTemplateTypeField.METADATA_TEMPLATE, + template_key: ClassificationTemplateTemplateKeyField = ClassificationTemplateTemplateKeyField.SECURITYCLASSIFICATION_6VMVOCHWUWO, + display_name: ClassificationTemplateDisplayNameField = ClassificationTemplateDisplayNameField.CLASSIFICATION, + hidden: Optional[bool] = None, + copy_instance_on_item_copy: Optional[bool] = None, + **kwargs + ): + """ + :param id: The ID of the classification template. + :type id: str + :param scope: The scope of the classification template. This is in the format + `enterprise_{id}` where the `id` is the enterprise ID. + :type scope: str + :param fields: A list of fields for this classification template. This includes + only one field, the `Box__Security__Classification__Key`, which defines + the different classifications available in this enterprise. + :type fields: List[ClassificationTemplateFieldsField] + :param type: The value will always be `metadata_template`., defaults to ClassificationTemplateTypeField.METADATA_TEMPLATE + :type type: ClassificationTemplateTypeField, optional + :param template_key: The value will always be `securityClassification-6VMVochwUWo`., defaults to ClassificationTemplateTemplateKeyField.SECURITYCLASSIFICATION_6VMVOCHWUWO + :type template_key: ClassificationTemplateTemplateKeyField, optional + :param display_name: The name of this template as shown in web and mobile interfaces., defaults to ClassificationTemplateDisplayNameField.CLASSIFICATION + :type display_name: ClassificationTemplateDisplayNameField, optional + :param hidden: Determines if the + template is always available in web and mobile interfaces., defaults to None + :type hidden: Optional[bool], optional + :param copy_instance_on_item_copy: Determines if + classifications are + copied along when the file or folder is + copied., defaults to None + :type copy_instance_on_item_copy: Optional[bool], optional + """ + super().__init__(**kwargs) + self.id = id + self.scope = scope + self.fields = fields + self.type = type + self.template_key = template_key + self.display_name = display_name + self.hidden = hidden + self.copy_instance_on_item_copy = copy_instance_on_item_copy diff --git a/box_sdk_gen/schemas/client_error.py b/box_sdk_gen/schemas/client_error.py new file mode 100644 index 000000000..fce9bae17 --- /dev/null +++ b/box_sdk_gen/schemas/client_error.py @@ -0,0 +1,77 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ClientErrorTypeField(str, Enum): + ERROR = 'error' + + +class ClientErrorCodeField(str, Enum): + CREATED = 'created' + ACCEPTED = 'accepted' + NO_CONTENT = 'no_content' + REDIRECT = 'redirect' + NOT_MODIFIED = 'not_modified' + BAD_REQUEST = 'bad_request' + UNAUTHORIZED = 'unauthorized' + FORBIDDEN = 'forbidden' + NOT_FOUND = 'not_found' + METHOD_NOT_ALLOWED = 'method_not_allowed' + CONFLICT = 'conflict' + PRECONDITION_FAILED = 'precondition_failed' + TOO_MANY_REQUESTS = 'too_many_requests' + INTERNAL_SERVER_ERROR = 'internal_server_error' + UNAVAILABLE = 'unavailable' + ITEM_NAME_INVALID = 'item_name_invalid' + INSUFFICIENT_SCOPE = 'insufficient_scope' + + +class ClientError(BaseObject): + _discriminator = 'type', {'error'} + + def __init__( + self, + *, + type: Optional[ClientErrorTypeField] = None, + status: Optional[int] = None, + code: Optional[ClientErrorCodeField] = None, + message: Optional[str] = None, + context_info: Optional[Dict] = None, + help_url: Optional[str] = None, + request_id: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `error`., defaults to None + :type type: Optional[ClientErrorTypeField], optional + :param status: The HTTP status of the response., defaults to None + :type status: Optional[int], optional + :param code: A Box-specific error code., defaults to None + :type code: Optional[ClientErrorCodeField], optional + :param message: A short message describing the error., defaults to None + :type message: Optional[str], optional + :param context_info: A free-form object that contains additional context + about the error. The possible fields are defined on + a per-endpoint basis. `message` is only one example., defaults to None + :type context_info: Optional[Dict], optional + :param help_url: A URL that links to more information about why this error occurred., defaults to None + :type help_url: Optional[str], optional + :param request_id: A unique identifier for this response, which can be used + when contacting Box support., defaults to None + :type request_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.status = status + self.code = code + self.message = message + self.context_info = context_info + self.help_url = help_url + self.request_id = request_id diff --git a/box_sdk_gen/schemas/collaboration.py b/box_sdk_gen/schemas/collaboration.py new file mode 100644 index 000000000..ef7ef0bca --- /dev/null +++ b/box_sdk_gen/schemas/collaboration.py @@ -0,0 +1,222 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file import File + +from box_sdk_gen.schemas.folder import Folder + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.group_mini import GroupMini + +from box_sdk_gen.schemas.collaboration_item import CollaborationItem + +from box_sdk_gen.schemas.app_item import AppItem + +from box_sdk_gen.schemas.collaboration_access_grantee import CollaborationAccessGrantee + +from box_sdk_gen.schemas.user_collaborations import UserCollaborations + +from box_sdk_gen.schemas.terms_of_service_base import TermsOfServiceBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class CollaborationTypeField(str, Enum): + COLLABORATION = 'collaboration' + + +class CollaborationRoleField(str, Enum): + EDITOR = 'editor' + VIEWER = 'viewer' + PREVIEWER = 'previewer' + UPLOADER = 'uploader' + PREVIEWER_UPLOADER = 'previewer uploader' + VIEWER_UPLOADER = 'viewer uploader' + CO_OWNER = 'co-owner' + OWNER = 'owner' + + +class CollaborationStatusField(str, Enum): + ACCEPTED = 'accepted' + PENDING = 'pending' + REJECTED = 'rejected' + + +class CollaborationAcceptanceRequirementsStatusTermsOfServiceRequirementField( + BaseObject +): + def __init__( + self, + *, + is_accepted: Optional[bool] = None, + terms_of_service: Optional[TermsOfServiceBase] = None, + **kwargs + ): + """ + :param is_accepted: Whether or not the terms of service have been accepted. The + field is `null` when there is no terms of service required., defaults to None + :type is_accepted: Optional[bool], optional + """ + super().__init__(**kwargs) + self.is_accepted = is_accepted + self.terms_of_service = terms_of_service + + +class CollaborationAcceptanceRequirementsStatusStrongPasswordRequirementField( + BaseObject +): + def __init__( + self, + *, + enterprise_has_strong_password_required_for_external_users: Optional[ + bool + ] = None, + user_has_strong_password: Optional[bool] = None, + **kwargs + ): + """ + :param enterprise_has_strong_password_required_for_external_users: Whether or not the enterprise that owns the content requires + a strong password to collaborate on the content, or enforces + an exposed password detection for the external collaborators., defaults to None + :type enterprise_has_strong_password_required_for_external_users: Optional[bool], optional + :param user_has_strong_password: Whether or not the user has a strong and not exposed password set + for their account. The field is `null` when a strong password is + not required., defaults to None + :type user_has_strong_password: Optional[bool], optional + """ + super().__init__(**kwargs) + self.enterprise_has_strong_password_required_for_external_users = ( + enterprise_has_strong_password_required_for_external_users + ) + self.user_has_strong_password = user_has_strong_password + + +class CollaborationAcceptanceRequirementsStatusTwoFactorAuthenticationRequirementField( + BaseObject +): + def __init__( + self, + *, + enterprise_has_two_factor_auth_enabled: Optional[bool] = None, + user_has_two_factor_authentication_enabled: Optional[bool] = None, + **kwargs + ): + """ + :param enterprise_has_two_factor_auth_enabled: Whether or not the enterprise that owns the content requires + two-factor authentication to be enabled in order to + collaborate on the content., defaults to None + :type enterprise_has_two_factor_auth_enabled: Optional[bool], optional + :param user_has_two_factor_authentication_enabled: Whether or not the user has two-factor authentication + enabled. The field is `null` when two-factor + authentication is not required., defaults to None + :type user_has_two_factor_authentication_enabled: Optional[bool], optional + """ + super().__init__(**kwargs) + self.enterprise_has_two_factor_auth_enabled = ( + enterprise_has_two_factor_auth_enabled + ) + self.user_has_two_factor_authentication_enabled = ( + user_has_two_factor_authentication_enabled + ) + + +class CollaborationAcceptanceRequirementsStatusField(BaseObject): + def __init__( + self, + *, + terms_of_service_requirement: Optional[ + CollaborationAcceptanceRequirementsStatusTermsOfServiceRequirementField + ] = None, + strong_password_requirement: Optional[ + CollaborationAcceptanceRequirementsStatusStrongPasswordRequirementField + ] = None, + two_factor_authentication_requirement: Optional[ + CollaborationAcceptanceRequirementsStatusTwoFactorAuthenticationRequirementField + ] = None, + **kwargs + ): + super().__init__(**kwargs) + self.terms_of_service_requirement = terms_of_service_requirement + self.strong_password_requirement = strong_password_requirement + self.two_factor_authentication_requirement = ( + two_factor_authentication_requirement + ) + + +class Collaboration(BaseObject): + _discriminator = 'type', {'collaboration'} + + def __init__( + self, + id: str, + *, + type: CollaborationTypeField = CollaborationTypeField.COLLABORATION, + item: Optional[CollaborationItem] = None, + app_item: Optional[AppItem] = None, + accessible_by: Optional[CollaborationAccessGrantee] = None, + invite_email: Optional[str] = None, + role: Optional[CollaborationRoleField] = None, + expires_at: Optional[DateTime] = None, + is_access_only: Optional[bool] = None, + status: Optional[CollaborationStatusField] = None, + acknowledged_at: Optional[DateTime] = None, + created_by: Optional[UserCollaborations] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + acceptance_requirements_status: Optional[ + CollaborationAcceptanceRequirementsStatusField + ] = None, + **kwargs + ): + """ + :param id: The unique identifier for this collaboration. + :type id: str + :param type: The value will always be `collaboration`., defaults to CollaborationTypeField.COLLABORATION + :type type: CollaborationTypeField, optional + :param invite_email: The email address used to invite an unregistered collaborator, if + they are not a registered user., defaults to None + :type invite_email: Optional[str], optional + :param role: The level of access granted., defaults to None + :type role: Optional[CollaborationRoleField], optional + :param expires_at: When the collaboration will expire, or `null` if no expiration + date is set., defaults to None + :type expires_at: Optional[DateTime], optional + :param is_access_only: If set to `true`, collaborators have access to + shared items, but such items won't be visible in the + All Files list. Additionally, collaborators won't + see the path to the root folder for the + shared item., defaults to None + :type is_access_only: Optional[bool], optional + :param status: The status of the collaboration invitation. If the status + is `pending`, `login` and `name` return an empty string., defaults to None + :type status: Optional[CollaborationStatusField], optional + :param acknowledged_at: When the `status` of the collaboration object changed to + `accepted` or `rejected`., defaults to None + :type acknowledged_at: Optional[DateTime], optional + :param created_at: When the collaboration object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the collaboration object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.item = item + self.app_item = app_item + self.accessible_by = accessible_by + self.invite_email = invite_email + self.role = role + self.expires_at = expires_at + self.is_access_only = is_access_only + self.status = status + self.acknowledged_at = acknowledged_at + self.created_by = created_by + self.created_at = created_at + self.modified_at = modified_at + self.acceptance_requirements_status = acceptance_requirements_status diff --git a/box_sdk_gen/schemas/collaboration_access_grantee.py b/box_sdk_gen/schemas/collaboration_access_grantee.py new file mode 100644 index 000000000..420bd32fb --- /dev/null +++ b/box_sdk_gen/schemas/collaboration_access_grantee.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.user_collaborations import UserCollaborations + +from box_sdk_gen.schemas.group_mini import GroupMini + +from box_sdk_gen.box.errors import BoxSDKError + +CollaborationAccessGrantee = Union[UserCollaborations, GroupMini] diff --git a/box_sdk_gen/schemas/collaboration_allowlist_entries.py b/box_sdk_gen/schemas/collaboration_allowlist_entries.py new file mode 100644 index 000000000..6d15e2629 --- /dev/null +++ b/box_sdk_gen/schemas/collaboration_allowlist_entries.py @@ -0,0 +1,40 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.collaboration_allowlist_entry import ( + CollaborationAllowlistEntry, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class CollaborationAllowlistEntries(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[CollaborationAllowlistEntry]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of allowed collaboration domains., defaults to None + :type entries: Optional[List[CollaborationAllowlistEntry]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/collaboration_allowlist_entry.py b/box_sdk_gen/schemas/collaboration_allowlist_entry.py new file mode 100644 index 000000000..341f79c9d --- /dev/null +++ b/box_sdk_gen/schemas/collaboration_allowlist_entry.py @@ -0,0 +1,83 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class CollaborationAllowlistEntryTypeField(str, Enum): + COLLABORATION_WHITELIST_ENTRY = 'collaboration_whitelist_entry' + + +class CollaborationAllowlistEntryDirectionField(str, Enum): + INBOUND = 'inbound' + OUTBOUND = 'outbound' + BOTH = 'both' + + +class CollaborationAllowlistEntryEnterpriseTypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class CollaborationAllowlistEntryEnterpriseField(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CollaborationAllowlistEntryEnterpriseTypeField] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this enterprise., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[CollaborationAllowlistEntryEnterpriseTypeField], optional + :param name: The name of the enterprise., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + + +class CollaborationAllowlistEntry(BaseObject): + _discriminator = 'type', {'collaboration_whitelist_entry'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CollaborationAllowlistEntryTypeField] = None, + domain: Optional[str] = None, + direction: Optional[CollaborationAllowlistEntryDirectionField] = None, + enterprise: Optional[CollaborationAllowlistEntryEnterpriseField] = None, + created_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param id: The unique identifier for this entry., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `collaboration_whitelist_entry`., defaults to None + :type type: Optional[CollaborationAllowlistEntryTypeField], optional + :param domain: The whitelisted domain., defaults to None + :type domain: Optional[str], optional + :param direction: The direction of the collaborations to allow., defaults to None + :type direction: Optional[CollaborationAllowlistEntryDirectionField], optional + :param created_at: The time the entry was created at., defaults to None + :type created_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.domain = domain + self.direction = direction + self.enterprise = enterprise + self.created_at = created_at diff --git a/box_sdk_gen/schemas/collaboration_allowlist_exempt_target.py b/box_sdk_gen/schemas/collaboration_allowlist_exempt_target.py new file mode 100644 index 000000000..ab1c17e38 --- /dev/null +++ b/box_sdk_gen/schemas/collaboration_allowlist_exempt_target.py @@ -0,0 +1,77 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class CollaborationAllowlistExemptTargetTypeField(str, Enum): + COLLABORATION_WHITELIST_EXEMPT_TARGET = 'collaboration_whitelist_exempt_target' + + +class CollaborationAllowlistExemptTargetEnterpriseTypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class CollaborationAllowlistExemptTargetEnterpriseField(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CollaborationAllowlistExemptTargetEnterpriseTypeField] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this enterprise., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[CollaborationAllowlistExemptTargetEnterpriseTypeField], optional + :param name: The name of the enterprise., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + + +class CollaborationAllowlistExemptTarget(BaseObject): + _discriminator = 'type', {'collaboration_whitelist_exempt_target'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CollaborationAllowlistExemptTargetTypeField] = None, + enterprise: Optional[CollaborationAllowlistExemptTargetEnterpriseField] = None, + user: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param id: The unique identifier for this exemption., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `collaboration_whitelist_exempt_target`., defaults to None + :type type: Optional[CollaborationAllowlistExemptTargetTypeField], optional + :param created_at: The time the entry was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The time the entry was modified., defaults to None + :type modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.enterprise = enterprise + self.user = user + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/collaboration_allowlist_exempt_targets.py b/box_sdk_gen/schemas/collaboration_allowlist_exempt_targets.py new file mode 100644 index 000000000..0c5fbc3a4 --- /dev/null +++ b/box_sdk_gen/schemas/collaboration_allowlist_exempt_targets.py @@ -0,0 +1,42 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.collaboration_allowlist_exempt_target import ( + CollaborationAllowlistExemptTarget, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class CollaborationAllowlistExemptTargets(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[CollaborationAllowlistExemptTarget]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of users exempt from any of the restrictions + imposed by the list of allowed collaboration domains + for this enterprise., defaults to None + :type entries: Optional[List[CollaborationAllowlistExemptTarget]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/collaboration_item.py b/box_sdk_gen/schemas/collaboration_item.py new file mode 100644 index 000000000..f1089f08d --- /dev/null +++ b/box_sdk_gen/schemas/collaboration_item.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.file import File + +from box_sdk_gen.schemas.folder import Folder + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.box.errors import BoxSDKError + +CollaborationItem = Union[File, Folder, WebLink] diff --git a/box_sdk_gen/schemas/collaborations.py b/box_sdk_gen/schemas/collaborations.py new file mode 100644 index 000000000..5ee9a17f8 --- /dev/null +++ b/box_sdk_gen/schemas/collaborations.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.collaboration import Collaboration + +from box_sdk_gen.box.errors import BoxSDKError + + +class Collaborations(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[Collaboration]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of collaborations., defaults to None + :type entries: Optional[List[Collaboration]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/collaborations_offset_paginated.py b/box_sdk_gen/schemas/collaborations_offset_paginated.py new file mode 100644 index 000000000..91538ad37 --- /dev/null +++ b/box_sdk_gen/schemas/collaborations_offset_paginated.py @@ -0,0 +1,47 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.collaboration import Collaboration + +from box_sdk_gen.box.errors import BoxSDKError + + +class CollaborationsOffsetPaginated(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + entries: Optional[List[Collaboration]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param entries: A list of collaborations., defaults to None + :type entries: Optional[List[Collaboration]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.entries = entries diff --git a/box_sdk_gen/schemas/collaborator_variable.py b/box_sdk_gen/schemas/collaborator_variable.py new file mode 100644 index 000000000..440ccf79c --- /dev/null +++ b/box_sdk_gen/schemas/collaborator_variable.py @@ -0,0 +1,68 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + + +class CollaboratorVariableTypeField(str, Enum): + VARIABLE = 'variable' + + +class CollaboratorVariableVariableTypeField(str, Enum): + USER_LIST = 'user_list' + + +class CollaboratorVariableVariableValueTypeField(str, Enum): + USER = 'user' + + +class CollaboratorVariableVariableValueField(BaseObject): + _discriminator = 'type', {'user'} + + def __init__( + self, + id: str, + *, + type: CollaboratorVariableVariableValueTypeField = CollaboratorVariableVariableValueTypeField.USER, + **kwargs + ): + """ + :param id: User's ID. + :type id: str + :param type: The object type., defaults to CollaboratorVariableVariableValueTypeField.USER + :type type: CollaboratorVariableVariableValueTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class CollaboratorVariable(BaseObject): + _discriminator = 'type', {'variable'} + + def __init__( + self, + variable_value: List[CollaboratorVariableVariableValueField], + *, + type: CollaboratorVariableTypeField = CollaboratorVariableTypeField.VARIABLE, + variable_type: CollaboratorVariableVariableTypeField = CollaboratorVariableVariableTypeField.USER_LIST, + **kwargs + ): + """ + :param variable_value: A list of user IDs. + :type variable_value: List[CollaboratorVariableVariableValueField] + :param type: Collaborator + object type., defaults to CollaboratorVariableTypeField.VARIABLE + :type type: CollaboratorVariableTypeField, optional + :param variable_type: Variable type + for the Collaborator + object., defaults to CollaboratorVariableVariableTypeField.USER_LIST + :type variable_type: CollaboratorVariableVariableTypeField, optional + """ + super().__init__(**kwargs) + self.variable_value = variable_value + self.type = type + self.variable_type = variable_type diff --git a/box_sdk_gen/schemas/collection.py b/box_sdk_gen/schemas/collection.py new file mode 100644 index 000000000..d55609279 --- /dev/null +++ b/box_sdk_gen/schemas/collection.py @@ -0,0 +1,50 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class CollectionTypeField(str, Enum): + COLLECTION = 'collection' + + +class CollectionNameField(str, Enum): + FAVORITES = 'Favorites' + + +class CollectionCollectionTypeField(str, Enum): + FAVORITES = 'favorites' + + +class Collection(BaseObject): + _discriminator = 'type', {'collection'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CollectionTypeField] = None, + name: Optional[CollectionNameField] = None, + collection_type: Optional[CollectionCollectionTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this collection., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `collection`., defaults to None + :type type: Optional[CollectionTypeField], optional + :param name: The name of the collection., defaults to None + :type name: Optional[CollectionNameField], optional + :param collection_type: The type of the collection. This is used to + determine the proper visual treatment for + collections., defaults to None + :type collection_type: Optional[CollectionCollectionTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + self.collection_type = collection_type diff --git a/box_sdk_gen/schemas/collections.py b/box_sdk_gen/schemas/collections.py new file mode 100644 index 000000000..b64669f5d --- /dev/null +++ b/box_sdk_gen/schemas/collections.py @@ -0,0 +1,80 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.collection import Collection + +from box_sdk_gen.box.errors import BoxSDKError + + +class CollectionsOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class CollectionsOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[CollectionsOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[CollectionsOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class Collections(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[CollectionsOrderField]] = None, + entries: Optional[List[Collection]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[CollectionsOrderField]], optional + :param entries: A list of collections., defaults to None + :type entries: Optional[List[Collection]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/comment.py b/box_sdk_gen/schemas/comment.py new file mode 100644 index 000000000..a67ba328f --- /dev/null +++ b/box_sdk_gen/schemas/comment.py @@ -0,0 +1,66 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.comment_base import CommentBaseTypeField + +from box_sdk_gen.schemas.comment_base import CommentBase + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class CommentItemField(BaseObject): + def __init__( + self, *, id: Optional[str] = None, type: Optional[str] = None, **kwargs + ): + """ + :param id: The unique identifier for this object., defaults to None + :type id: Optional[str], optional + :param type: The type for this object., defaults to None + :type type: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class Comment(CommentBase): + def __init__( + self, + *, + is_reply_comment: Optional[bool] = None, + message: Optional[str] = None, + created_by: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + item: Optional[CommentItemField] = None, + id: Optional[str] = None, + type: Optional[CommentBaseTypeField] = None, + **kwargs + ): + """ + :param is_reply_comment: Whether or not this comment is a reply to another + comment., defaults to None + :type is_reply_comment: Optional[bool], optional + :param message: The text of the comment, as provided by the user., defaults to None + :type message: Optional[str], optional + :param created_at: The time this comment was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The time this comment was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param id: The unique identifier for this comment., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `comment`., defaults to None + :type type: Optional[CommentBaseTypeField], optional + """ + super().__init__(id=id, type=type, **kwargs) + self.is_reply_comment = is_reply_comment + self.message = message + self.created_by = created_by + self.created_at = created_at + self.modified_at = modified_at + self.item = item diff --git a/box_sdk_gen/schemas/comment_base.py b/box_sdk_gen/schemas/comment_base.py new file mode 100644 index 000000000..bb5761693 --- /dev/null +++ b/box_sdk_gen/schemas/comment_base.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class CommentBaseTypeField(str, Enum): + COMMENT = 'comment' + + +class CommentBase(BaseObject): + _discriminator = 'type', {'comment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[CommentBaseTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this comment., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `comment`., defaults to None + :type type: Optional[CommentBaseTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/comment_full.py b/box_sdk_gen/schemas/comment_full.py new file mode 100644 index 000000000..1b3b7b69f --- /dev/null +++ b/box_sdk_gen/schemas/comment_full.py @@ -0,0 +1,64 @@ +from typing import Optional + +from box_sdk_gen.schemas.comment_base import CommentBaseTypeField + +from box_sdk_gen.schemas.comment_base import CommentBase + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.comment import CommentItemField + +from box_sdk_gen.schemas.comment import Comment + +from box_sdk_gen.box.errors import BoxSDKError + + +class CommentFull(Comment): + def __init__( + self, + *, + tagged_message: Optional[str] = None, + is_reply_comment: Optional[bool] = None, + message: Optional[str] = None, + created_by: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + item: Optional[CommentItemField] = None, + id: Optional[str] = None, + type: Optional[CommentBaseTypeField] = None, + **kwargs + ): + """ + :param tagged_message: The string representing the comment text with + @mentions included. @mention format is @[id:username] + where `id` is user's Box ID and `username` is + their display name., defaults to None + :type tagged_message: Optional[str], optional + :param is_reply_comment: Whether or not this comment is a reply to another + comment., defaults to None + :type is_reply_comment: Optional[bool], optional + :param message: The text of the comment, as provided by the user., defaults to None + :type message: Optional[str], optional + :param created_at: The time this comment was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The time this comment was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param id: The unique identifier for this comment., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `comment`., defaults to None + :type type: Optional[CommentBaseTypeField], optional + """ + super().__init__( + is_reply_comment=is_reply_comment, + message=message, + created_by=created_by, + created_at=created_at, + modified_at=modified_at, + item=item, + id=id, + type=type, + **kwargs + ) + self.tagged_message = tagged_message diff --git a/box_sdk_gen/schemas/comments.py b/box_sdk_gen/schemas/comments.py new file mode 100644 index 000000000..979b5afb4 --- /dev/null +++ b/box_sdk_gen/schemas/comments.py @@ -0,0 +1,80 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.comment_full import CommentFull + +from box_sdk_gen.box.errors import BoxSDKError + + +class CommentsOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class CommentsOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[CommentsOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[CommentsOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class Comments(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[CommentsOrderField]] = None, + entries: Optional[List[CommentFull]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[CommentsOrderField]], optional + :param entries: A list of comments., defaults to None + :type entries: Optional[List[CommentFull]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/completion_rule_variable.py b/box_sdk_gen/schemas/completion_rule_variable.py new file mode 100644 index 000000000..67b818f3f --- /dev/null +++ b/box_sdk_gen/schemas/completion_rule_variable.py @@ -0,0 +1,48 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class CompletionRuleVariableTypeField(str, Enum): + VARIABLE = 'variable' + + +class CompletionRuleVariableVariableTypeField(str, Enum): + TASK_COMPLETION_RULE = 'task_completion_rule' + + +class CompletionRuleVariableVariableValueField(str, Enum): + ALL_ASSIGNEES = 'all_assignees' + ANY_ASSIGNEES = 'any_assignees' + + +class CompletionRuleVariable(BaseObject): + _discriminator = 'type', {'variable'} + + def __init__( + self, + variable_value: CompletionRuleVariableVariableValueField, + *, + type: CompletionRuleVariableTypeField = CompletionRuleVariableTypeField.VARIABLE, + variable_type: CompletionRuleVariableVariableTypeField = CompletionRuleVariableVariableTypeField.TASK_COMPLETION_RULE, + **kwargs + ): + """ + :param variable_value: Variable + values for a completion + rule. + :type variable_value: CompletionRuleVariableVariableValueField + :param type: Completion + Rule object type., defaults to CompletionRuleVariableTypeField.VARIABLE + :type type: CompletionRuleVariableTypeField, optional + :param variable_type: Variable type + for the Completion + Rule object., defaults to CompletionRuleVariableVariableTypeField.TASK_COMPLETION_RULE + :type variable_type: CompletionRuleVariableVariableTypeField, optional + """ + super().__init__(**kwargs) + self.variable_value = variable_value + self.type = type + self.variable_type = variable_type diff --git a/box_sdk_gen/schemas/conflict_error.py b/box_sdk_gen/schemas/conflict_error.py new file mode 100644 index 000000000..4ac1341c5 --- /dev/null +++ b/box_sdk_gen/schemas/conflict_error.py @@ -0,0 +1,71 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.schemas.client_error import ClientErrorTypeField + +from box_sdk_gen.schemas.client_error import ClientErrorCodeField + +from box_sdk_gen.schemas.client_error import ClientError + +from box_sdk_gen.schemas.file_conflict import FileConflict + +from box_sdk_gen.box.errors import BoxSDKError + + +class ConflictErrorContextInfoField(BaseObject): + def __init__(self, *, conflicts: Optional[List[FileConflict]] = None, **kwargs): + """ + :param conflicts: A list of the file conflicts that caused this error., defaults to None + :type conflicts: Optional[List[FileConflict]], optional + """ + super().__init__(**kwargs) + self.conflicts = conflicts + + +class ConflictError(ClientError): + def __init__( + self, + *, + type: Optional[ClientErrorTypeField] = None, + status: Optional[int] = None, + code: Optional[ClientErrorCodeField] = None, + message: Optional[str] = None, + context_info: Optional[Dict] = None, + help_url: Optional[str] = None, + request_id: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `error`., defaults to None + :type type: Optional[ClientErrorTypeField], optional + :param status: The HTTP status of the response., defaults to None + :type status: Optional[int], optional + :param code: A Box-specific error code., defaults to None + :type code: Optional[ClientErrorCodeField], optional + :param message: A short message describing the error., defaults to None + :type message: Optional[str], optional + :param context_info: A free-form object that contains additional context + about the error. The possible fields are defined on + a per-endpoint basis. `message` is only one example., defaults to None + :type context_info: Optional[Dict], optional + :param help_url: A URL that links to more information about why this error occurred., defaults to None + :type help_url: Optional[str], optional + :param request_id: A unique identifier for this response, which can be used + when contacting Box support., defaults to None + :type request_id: Optional[str], optional + """ + super().__init__( + type=type, + status=status, + code=code, + message=message, + context_info=context_info, + help_url=help_url, + request_id=request_id, + **kwargs + ) diff --git a/box_sdk_gen/schemas/create_ai_agent.py b/box_sdk_gen/schemas/create_ai_agent.py new file mode 100644 index 000000000..caa2a694d --- /dev/null +++ b/box_sdk_gen/schemas/create_ai_agent.py @@ -0,0 +1,65 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.schemas.ai_agent_allowed_entity import AiAgentAllowedEntity + +from box_sdk_gen.schemas.ai_studio_agent_ask import AiStudioAgentAsk + +from box_sdk_gen.schemas.ai_studio_agent_text_gen import AiStudioAgentTextGen + +from box_sdk_gen.schemas.ai_studio_agent_extract import AiStudioAgentExtract + +from box_sdk_gen.box.errors import BoxSDKError + + +class CreateAiAgentTypeField(str, Enum): + AI_AGENT = 'ai_agent' + + +class CreateAiAgent(BaseObject): + _discriminator = 'type', {'ai_agent'} + + def __init__( + self, + name: str, + access_state: str, + *, + type: CreateAiAgentTypeField = CreateAiAgentTypeField.AI_AGENT, + icon_reference: Optional[str] = None, + allowed_entities: Optional[List[AiAgentAllowedEntity]] = None, + ask: Optional[AiStudioAgentAsk] = None, + text_gen: Optional[AiStudioAgentTextGen] = None, + extract: Optional[AiStudioAgentExtract] = None, + **kwargs + ): + """ + :param name: The name of the AI Agent. + :type name: str + :param access_state: The state of the AI Agent. Possible values are: `enabled`, `disabled`, and `enabled_for_selected_users`. + :type access_state: str + :param type: The type of agent used to handle queries., defaults to CreateAiAgentTypeField.AI_AGENT + :type type: CreateAiAgentTypeField, optional + :param icon_reference: The icon reference of the AI Agent. It should have format of the URL `https://cdn01.boxcdn.net/app-assets/aistudio/avatars/` + where possible values of `file_name` are: `logo_boxAi.png`,`logo_stamp.png`,`logo_legal.png`,`logo_finance.png`,`logo_config.png`,`logo_handshake.png`,`logo_analytics.png`,`logo_classification.png`., defaults to None + :type icon_reference: Optional[str], optional + :param allowed_entities: List of allowed users or groups., defaults to None + :type allowed_entities: Optional[List[AiAgentAllowedEntity]], optional + """ + super().__init__(**kwargs) + self.name = name + self.access_state = access_state + self.type = type + self.icon_reference = icon_reference + self.allowed_entities = allowed_entities + self.ask = ask + self.text_gen = text_gen + self.extract = extract diff --git a/box_sdk_gen/schemas/device_pinner.py b/box_sdk_gen/schemas/device_pinner.py new file mode 100644 index 000000000..97652f005 --- /dev/null +++ b/box_sdk_gen/schemas/device_pinner.py @@ -0,0 +1,40 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class DevicePinnerTypeField(str, Enum): + DEVICE_PINNER = 'device_pinner' + + +class DevicePinner(BaseObject): + _discriminator = 'type', {'device_pinner'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[DevicePinnerTypeField] = None, + owned_by: Optional[UserMini] = None, + product_name: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this device pin., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `device_pinner`., defaults to None + :type type: Optional[DevicePinnerTypeField], optional + :param product_name: The type of device being pinned., defaults to None + :type product_name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.owned_by = owned_by + self.product_name = product_name diff --git a/box_sdk_gen/schemas/device_pinners.py b/box_sdk_gen/schemas/device_pinners.py new file mode 100644 index 000000000..3ad323efa --- /dev/null +++ b/box_sdk_gen/schemas/device_pinners.py @@ -0,0 +1,68 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.device_pinner import DevicePinner + +from box_sdk_gen.box.errors import BoxSDKError + + +class DevicePinnersOrderByField(str, Enum): + ID = 'id' + + +class DevicePinnersOrderDirectionField(str, Enum): + ASC = 'asc' + DESC = 'desc' + + +class DevicePinnersOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[DevicePinnersOrderByField] = None, + direction: Optional[DevicePinnersOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field that is ordered by., defaults to None + :type by: Optional[DevicePinnersOrderByField], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[DevicePinnersOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class DevicePinners(BaseObject): + def __init__( + self, + *, + entries: Optional[List[DevicePinner]] = None, + limit: Optional[int] = None, + next_marker: Optional[int] = None, + order: Optional[List[DevicePinnersOrderField]] = None, + **kwargs + ): + """ + :param entries: A list of device pins., defaults to None + :type entries: Optional[List[DevicePinner]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[int], optional + :param order: The order by which items are returned., defaults to None + :type order: Optional[List[DevicePinnersOrderField]], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker + self.order = order diff --git a/box_sdk_gen/schemas/email_alias.py b/box_sdk_gen/schemas/email_alias.py new file mode 100644 index 000000000..b8355f6e5 --- /dev/null +++ b/box_sdk_gen/schemas/email_alias.py @@ -0,0 +1,40 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class EmailAliasTypeField(str, Enum): + EMAIL_ALIAS = 'email_alias' + + +class EmailAlias(BaseObject): + _discriminator = 'type', {'email_alias'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[EmailAliasTypeField] = None, + email: Optional[str] = None, + is_confirmed: Optional[bool] = None, + **kwargs + ): + """ + :param id: The unique identifier for this object., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `email_alias`., defaults to None + :type type: Optional[EmailAliasTypeField], optional + :param email: The email address., defaults to None + :type email: Optional[str], optional + :param is_confirmed: Whether the email address has been confirmed., defaults to None + :type is_confirmed: Optional[bool], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.email = email + self.is_confirmed = is_confirmed diff --git a/box_sdk_gen/schemas/email_aliases.py b/box_sdk_gen/schemas/email_aliases.py new file mode 100644 index 000000000..d359665b3 --- /dev/null +++ b/box_sdk_gen/schemas/email_aliases.py @@ -0,0 +1,28 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.email_alias import EmailAlias + +from box_sdk_gen.box.errors import BoxSDKError + + +class EmailAliases(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + entries: Optional[List[EmailAlias]] = None, + **kwargs + ): + """ + :param total_count: The number of email aliases., defaults to None + :type total_count: Optional[int], optional + :param entries: A list of email aliases., defaults to None + :type entries: Optional[List[EmailAlias]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries diff --git a/box_sdk_gen/schemas/enterprise_base.py b/box_sdk_gen/schemas/enterprise_base.py new file mode 100644 index 000000000..70868168b --- /dev/null +++ b/box_sdk_gen/schemas/enterprise_base.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class EnterpriseBaseTypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class EnterpriseBase(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[EnterpriseBaseTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this enterprise., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[EnterpriseBaseTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/event.py b/box_sdk_gen/schemas/event.py new file mode 100644 index 000000000..eccdf7582 --- /dev/null +++ b/box_sdk_gen/schemas/event.py @@ -0,0 +1,237 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from box_sdk_gen.schemas.user import User + +from box_sdk_gen.schemas.event_source import EventSource + +from box_sdk_gen.schemas.file import File + +from box_sdk_gen.schemas.folder import Folder + +from box_sdk_gen.schemas.app_item_event_source import AppItemEventSource + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.event_source_resource import EventSourceResource + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class EventEventTypeField(str, Enum): + ACCESS_GRANTED = 'ACCESS_GRANTED' + ACCESS_REVOKED = 'ACCESS_REVOKED' + ADD_DEVICE_ASSOCIATION = 'ADD_DEVICE_ASSOCIATION' + ADD_LOGIN_ACTIVITY_DEVICE = 'ADD_LOGIN_ACTIVITY_DEVICE' + ADMIN_LOGIN = 'ADMIN_LOGIN' + APPLICATION_CREATED = 'APPLICATION_CREATED' + APPLICATION_PUBLIC_KEY_ADDED = 'APPLICATION_PUBLIC_KEY_ADDED' + APPLICATION_PUBLIC_KEY_DELETED = 'APPLICATION_PUBLIC_KEY_DELETED' + CHANGE_ADMIN_ROLE = 'CHANGE_ADMIN_ROLE' + CHANGE_FOLDER_PERMISSION = 'CHANGE_FOLDER_PERMISSION' + COLLABORATION_ACCEPT = 'COLLABORATION_ACCEPT' + COLLABORATION_EXPIRATION = 'COLLABORATION_EXPIRATION' + COLLABORATION_INVITE = 'COLLABORATION_INVITE' + COLLABORATION_REMOVE = 'COLLABORATION_REMOVE' + COLLABORATION_ROLE_CHANGE = 'COLLABORATION_ROLE_CHANGE' + COLLAB_ADD_COLLABORATOR = 'COLLAB_ADD_COLLABORATOR' + COLLAB_INVITE_COLLABORATOR = 'COLLAB_INVITE_COLLABORATOR' + COLLAB_REMOVE_COLLABORATOR = 'COLLAB_REMOVE_COLLABORATOR' + COLLAB_ROLE_CHANGE = 'COLLAB_ROLE_CHANGE' + COMMENT_CREATE = 'COMMENT_CREATE' + COMMENT_DELETE = 'COMMENT_DELETE' + CONTENT_ACCESS = 'CONTENT_ACCESS' + CONTENT_WORKFLOW_ABNORMAL_DOWNLOAD_ACTIVITY = ( + 'CONTENT_WORKFLOW_ABNORMAL_DOWNLOAD_ACTIVITY' + ) + CONTENT_WORKFLOW_AUTOMATION_ADD = 'CONTENT_WORKFLOW_AUTOMATION_ADD' + CONTENT_WORKFLOW_AUTOMATION_DELETE = 'CONTENT_WORKFLOW_AUTOMATION_DELETE' + CONTENT_WORKFLOW_POLICY_ADD = 'CONTENT_WORKFLOW_POLICY_ADD' + CONTENT_WORKFLOW_SHARING_POLICY_VIOLATION = ( + 'CONTENT_WORKFLOW_SHARING_POLICY_VIOLATION' + ) + CONTENT_WORKFLOW_UPLOAD_POLICY_VIOLATION = ( + 'CONTENT_WORKFLOW_UPLOAD_POLICY_VIOLATION' + ) + COPY = 'COPY' + DATA_RETENTION_CREATE_RETENTION = 'DATA_RETENTION_CREATE_RETENTION' + DATA_RETENTION_REMOVE_RETENTION = 'DATA_RETENTION_REMOVE_RETENTION' + DELETE = 'DELETE' + DELETE_USER = 'DELETE_USER' + DEVICE_TRUST_CHECK_FAILED = 'DEVICE_TRUST_CHECK_FAILED' + DOWNLOAD = 'DOWNLOAD' + EDIT = 'EDIT' + EDIT_USER = 'EDIT_USER' + EDR_CROWDSTRIKE_DEVICE_DETECTED = 'EDR_CROWDSTRIKE_DEVICE_DETECTED' + EDR_CROWDSTRIKE_NO_BOX_TOOLS = 'EDR_CROWDSTRIKE_NO_BOX_TOOLS' + EDR_CROWDSTRIKE_BOX_TOOLS_OUTDATED = 'EDR_CROWDSTRIKE_BOX_TOOLS_OUTDATED' + EDR_CROWDSTRIKE_DRIVE_OUTDATED = 'EDR_CROWDSTRIKE_DRIVE_OUTDATED' + EDR_CROWDSTRIKE_ACCESS_ALLOWED_NO_CROWDSTRIKE_DEVICE = ( + 'EDR_CROWDSTRIKE_ACCESS_ALLOWED_NO_CROWDSTRIKE_DEVICE' + ) + EDR_CROWDSTRIKE_ACCESS_REVOKED = 'EDR_CROWDSTRIKE_ACCESS_REVOKED' + EMAIL_ALIAS_CONFIRM = 'EMAIL_ALIAS_CONFIRM' + EMAIL_ALIAS_REMOVE = 'EMAIL_ALIAS_REMOVE' + ENABLE_TWO_FACTOR_AUTH = 'ENABLE_TWO_FACTOR_AUTH' + ENTERPRISE_APP_AUTHORIZATION_UPDATE = 'ENTERPRISE_APP_AUTHORIZATION_UPDATE' + FAILED_LOGIN = 'FAILED_LOGIN' + FILE_MARKED_MALICIOUS = 'FILE_MARKED_MALICIOUS' + FILE_WATERMARKED_DOWNLOAD = 'FILE_WATERMARKED_DOWNLOAD' + GROUP_ADD_ITEM = 'GROUP_ADD_ITEM' + GROUP_ADD_USER = 'GROUP_ADD_USER' + GROUP_CREATION = 'GROUP_CREATION' + GROUP_DELETION = 'GROUP_DELETION' + GROUP_EDITED = 'GROUP_EDITED' + GROUP_REMOVE_ITEM = 'GROUP_REMOVE_ITEM' + GROUP_REMOVE_USER = 'GROUP_REMOVE_USER' + ITEM_COPY = 'ITEM_COPY' + ITEM_CREATE = 'ITEM_CREATE' + ITEM_DOWNLOAD = 'ITEM_DOWNLOAD' + ITEM_EMAIL_SEND = 'ITEM_EMAIL_SEND' + ITEM_MAKE_CURRENT_VERSION = 'ITEM_MAKE_CURRENT_VERSION' + ITEM_MODIFY = 'ITEM_MODIFY' + ITEM_MOVE = 'ITEM_MOVE' + ITEM_OPEN = 'ITEM_OPEN' + ITEM_PREVIEW = 'ITEM_PREVIEW' + ITEM_RENAME = 'ITEM_RENAME' + ITEM_SHARED = 'ITEM_SHARED' + ITEM_SHARED_CREATE = 'ITEM_SHARED_CREATE' + ITEM_SHARED_UNSHARE = 'ITEM_SHARED_UNSHARE' + ITEM_SHARED_UPDATE = 'ITEM_SHARED_UPDATE' + ITEM_SYNC = 'ITEM_SYNC' + ITEM_TRASH = 'ITEM_TRASH' + ITEM_UNDELETE_VIA_TRASH = 'ITEM_UNDELETE_VIA_TRASH' + ITEM_UNSYNC = 'ITEM_UNSYNC' + ITEM_UPLOAD = 'ITEM_UPLOAD' + LEGAL_HOLD_ASSIGNMENT_CREATE = 'LEGAL_HOLD_ASSIGNMENT_CREATE' + LEGAL_HOLD_ASSIGNMENT_DELETE = 'LEGAL_HOLD_ASSIGNMENT_DELETE' + LEGAL_HOLD_POLICY_CREATE = 'LEGAL_HOLD_POLICY_CREATE' + LEGAL_HOLD_POLICY_DELETE = 'LEGAL_HOLD_POLICY_DELETE' + LEGAL_HOLD_POLICY_UPDATE = 'LEGAL_HOLD_POLICY_UPDATE' + LOCK = 'LOCK' + LOCK_CREATE = 'LOCK_CREATE' + LOCK_DESTROY = 'LOCK_DESTROY' + LOGIN = 'LOGIN' + MASTER_INVITE_ACCEPT = 'MASTER_INVITE_ACCEPT' + MASTER_INVITE_REJECT = 'MASTER_INVITE_REJECT' + METADATA_INSTANCE_CREATE = 'METADATA_INSTANCE_CREATE' + METADATA_INSTANCE_DELETE = 'METADATA_INSTANCE_DELETE' + METADATA_INSTANCE_UPDATE = 'METADATA_INSTANCE_UPDATE' + METADATA_TEMPLATE_CREATE = 'METADATA_TEMPLATE_CREATE' + METADATA_TEMPLATE_DELETE = 'METADATA_TEMPLATE_DELETE' + METADATA_TEMPLATE_UPDATE = 'METADATA_TEMPLATE_UPDATE' + MOVE = 'MOVE' + NEW_USER = 'NEW_USER' + PREVIEW = 'PREVIEW' + REMOVE_DEVICE_ASSOCIATION = 'REMOVE_DEVICE_ASSOCIATION' + REMOVE_LOGIN_ACTIVITY_DEVICE = 'REMOVE_LOGIN_ACTIVITY_DEVICE' + RENAME = 'RENAME' + RETENTION_POLICY_ASSIGNMENT_ADD = 'RETENTION_POLICY_ASSIGNMENT_ADD' + SHARE = 'SHARE' + SHARED_LINK_SEND = 'SHARED_LINK_SEND' + SHARE_EXPIRATION = 'SHARE_EXPIRATION' + SHIELD_ALERT = 'SHIELD_ALERT' + SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED = 'SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED' + SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION = ( + 'SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION' + ) + SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED = 'SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED' + SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION = ( + 'SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION' + ) + SHIELD_JUSTIFICATION_APPROVAL = 'SHIELD_JUSTIFICATION_APPROVAL' + SHIELD_SHARED_LINK_ACCESS_BLOCKED = 'SHIELD_SHARED_LINK_ACCESS_BLOCKED' + SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_CREATE = ( + 'SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_CREATE' + ) + SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_UPDATE = ( + 'SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_UPDATE' + ) + SIGN_DOCUMENT_ASSIGNED = 'SIGN_DOCUMENT_ASSIGNED' + SIGN_DOCUMENT_CANCELLED = 'SIGN_DOCUMENT_CANCELLED' + SIGN_DOCUMENT_COMPLETED = 'SIGN_DOCUMENT_COMPLETED' + SIGN_DOCUMENT_CONVERTED = 'SIGN_DOCUMENT_CONVERTED' + SIGN_DOCUMENT_CREATED = 'SIGN_DOCUMENT_CREATED' + SIGN_DOCUMENT_DECLINED = 'SIGN_DOCUMENT_DECLINED' + SIGN_DOCUMENT_EXPIRED = 'SIGN_DOCUMENT_EXPIRED' + SIGN_DOCUMENT_SIGNED = 'SIGN_DOCUMENT_SIGNED' + SIGN_DOCUMENT_VIEWED_BY_SIGNED = 'SIGN_DOCUMENT_VIEWED_BY_SIGNED' + SIGNER_DOWNLOADED = 'SIGNER_DOWNLOADED' + SIGNER_FORWARDED = 'SIGNER_FORWARDED' + STORAGE_EXPIRATION = 'STORAGE_EXPIRATION' + TAG_ITEM_CREATE = 'TAG_ITEM_CREATE' + TASK_ASSIGNMENT_CREATE = 'TASK_ASSIGNMENT_CREATE' + TASK_ASSIGNMENT_DELETE = 'TASK_ASSIGNMENT_DELETE' + TASK_ASSIGNMENT_UPDATE = 'TASK_ASSIGNMENT_UPDATE' + TASK_CREATE = 'TASK_CREATE' + TASK_UPDATE = 'TASK_UPDATE' + TERMS_OF_SERVICE_ACCEPT = 'TERMS_OF_SERVICE_ACCEPT' + TERMS_OF_SERVICE_REJECT = 'TERMS_OF_SERVICE_REJECT' + UNDELETE = 'UNDELETE' + UNLOCK = 'UNLOCK' + UNSHARE = 'UNSHARE' + UPDATE_COLLABORATION_EXPIRATION = 'UPDATE_COLLABORATION_EXPIRATION' + UPDATE_SHARE_EXPIRATION = 'UPDATE_SHARE_EXPIRATION' + UPLOAD = 'UPLOAD' + USER_AUTHENTICATE_OAUTH2_ACCESS_TOKEN_CREATE = ( + 'USER_AUTHENTICATE_OAUTH2_ACCESS_TOKEN_CREATE' + ) + WATERMARK_LABEL_CREATE = 'WATERMARK_LABEL_CREATE' + WATERMARK_LABEL_DELETE = 'WATERMARK_LABEL_DELETE' + + +class EventAdditionalDetailsField(BaseObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class Event(BaseObject): + def __init__( + self, + *, + type: Optional[str] = None, + created_at: Optional[DateTime] = None, + recorded_at: Optional[DateTime] = None, + event_id: Optional[str] = None, + created_by: Optional[UserMini] = None, + event_type: Optional[EventEventTypeField] = None, + session_id: Optional[str] = None, + source: Optional[EventSourceResource] = None, + additional_details: Optional[EventAdditionalDetailsField] = None, + **kwargs + ): + """ + :param type: The value will always be `event`., defaults to None + :type type: Optional[str], optional + :param created_at: When the event object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param recorded_at: When the event object was recorded in database., defaults to None + :type recorded_at: Optional[DateTime], optional + :param event_id: The ID of the event object. You can use this to detect duplicate events., defaults to None + :type event_id: Optional[str], optional + :param session_id: The session of the user that performed the action. Not all events will + populate this attribute., defaults to None + :type session_id: Optional[str], optional + :param additional_details: This object provides additional information about the event if available. + + This can include how a user performed an event as well as additional + information to correlate an event to external KeySafe logs. Not all events + have an `additional_details` object. This object is only available in the + Enterprise Events., defaults to None + :type additional_details: Optional[EventAdditionalDetailsField], optional + """ + super().__init__(**kwargs) + self.type = type + self.created_at = created_at + self.recorded_at = recorded_at + self.event_id = event_id + self.created_by = created_by + self.event_type = event_type + self.session_id = session_id + self.source = source + self.additional_details = additional_details diff --git a/box_sdk_gen/schemas/event_source.py b/box_sdk_gen/schemas/event_source.py new file mode 100644 index 000000000..513744f4c --- /dev/null +++ b/box_sdk_gen/schemas/event_source.py @@ -0,0 +1,63 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class EventSourceItemTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class EventSourceClassificationField(BaseObject): + def __init__(self, *, name: Optional[str] = None, **kwargs): + """ + :param name: The classification's name., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.name = name + + +class EventSource(BaseObject): + _discriminator = 'item_type', {'file', 'folder'} + + def __init__( + self, + item_type: EventSourceItemTypeField, + item_id: str, + item_name: str, + *, + classification: Optional[EventSourceClassificationField] = None, + parent: Optional[FolderMini] = None, + owned_by: Optional[UserMini] = None, + **kwargs + ): + """ + :param item_type: The type of the item that the event + represents. Can be `file` or `folder`. + :type item_type: EventSourceItemTypeField + :param item_id: The unique identifier that represents the + item. + :type item_id: str + :param item_name: The name of the item. + :type item_name: str + :param classification: The object containing classification information for the item that + triggered the event. This field will not appear if the item does not + have a classification set., defaults to None + :type classification: Optional[EventSourceClassificationField], optional + """ + super().__init__(**kwargs) + self.item_type = item_type + self.item_id = item_id + self.item_name = item_name + self.classification = classification + self.parent = parent + self.owned_by = owned_by diff --git a/box_sdk_gen/schemas/event_source_resource.py b/box_sdk_gen/schemas/event_source_resource.py new file mode 100644 index 000000000..abfcfe698 --- /dev/null +++ b/box_sdk_gen/schemas/event_source_resource.py @@ -0,0 +1,17 @@ +from typing import Union + +from typing import Dict + +from box_sdk_gen.schemas.user import User + +from box_sdk_gen.schemas.event_source import EventSource + +from box_sdk_gen.schemas.file import File + +from box_sdk_gen.schemas.folder import Folder + +from box_sdk_gen.schemas.app_item_event_source import AppItemEventSource + +from box_sdk_gen.box.errors import BoxSDKError + +EventSourceResource = Union[User, EventSource, File, Folder, Dict, AppItemEventSource] diff --git a/box_sdk_gen/schemas/events.py b/box_sdk_gen/schemas/events.py new file mode 100644 index 000000000..2e2e14a8d --- /dev/null +++ b/box_sdk_gen/schemas/events.py @@ -0,0 +1,35 @@ +from typing import Optional + +from typing import Union + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.event import Event + +from box_sdk_gen.box.errors import BoxSDKError + + +class Events(BaseObject): + def __init__( + self, + *, + chunk_size: Optional[int] = None, + next_stream_position: Optional[Union[str, int]] = None, + entries: Optional[List[Event]] = None, + **kwargs + ): + """ + :param chunk_size: The number of events returned in this response., defaults to None + :type chunk_size: Optional[int], optional + :param next_stream_position: The stream position of the start of the next page (chunk) + of events., defaults to None + :type next_stream_position: Optional[Union[str, int]], optional + :param entries: A list of events., defaults to None + :type entries: Optional[List[Event]], optional + """ + super().__init__(**kwargs) + self.chunk_size = chunk_size + self.next_stream_position = next_stream_position + self.entries = entries diff --git a/box_sdk_gen/schemas/file.py b/box_sdk_gen/schemas/file.py new file mode 100644 index 000000000..2e9344e88 --- /dev/null +++ b/box_sdk_gen/schemas/file.py @@ -0,0 +1,278 @@ +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.file_base import FileBaseTypeField + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FilePathCollectionField(BaseObject): + def __init__(self, total_count: int, entries: List[FolderMini], **kwargs): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: The parent folders for this item. + :type entries: List[FolderMini] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class FileSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class FileSharedLinkEffectiveAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class FileSharedLinkEffectivePermissionField(str, Enum): + CAN_EDIT = 'can_edit' + CAN_DOWNLOAD = 'can_download' + CAN_PREVIEW = 'can_preview' + NO_ACCESS = 'no_access' + + +class FileSharedLinkPermissionsField(BaseObject): + def __init__(self, can_download: bool, can_preview: bool, can_edit: bool, **kwargs): + """ + :param can_download: Defines if the shared link allows for the item to be downloaded. For + shared links on folders, this also applies to any items in the folder. + + This value can be set to `true` when the effective access level is + set to `open` or `company`, not `collaborators`. + :type can_download: bool + :param can_preview: Defines if the shared link allows for the item to be previewed. + + This value is always `true`. For shared links on folders this also + applies to any items in the folder. + :type can_preview: bool + :param can_edit: Defines if the shared link allows for the item to be edited. + + This value can only be `true` if `can_download` is also `true` and if + the item has a type of `file`. + :type can_edit: bool + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class FileSharedLinkField(BaseObject): + def __init__( + self, + url: str, + effective_access: FileSharedLinkEffectiveAccessField, + effective_permission: FileSharedLinkEffectivePermissionField, + is_password_enabled: bool, + download_count: int, + preview_count: int, + *, + download_url: Optional[str] = None, + vanity_url: Optional[str] = None, + vanity_name: Optional[str] = None, + access: Optional[FileSharedLinkAccessField] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[FileSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param url: The URL that can be used to access the item on Box. + + This URL will display the item in Box's preview UI where the file + can be downloaded if allowed. + + This URL will continue to work even when a custom `vanity_url` + has been set for this shared link. + :type url: str + :param effective_access: The effective access level for the shared link. This can be a more + restrictive access level than the value in the `access` field when the + enterprise settings restrict the allowed access levels. + :type effective_access: FileSharedLinkEffectiveAccessField + :param effective_permission: The effective permissions for this shared link. + These result in the more restrictive combination of + the share link permissions and the item permissions set + by the administrator, the owner, and any ancestor item + such as a folder. + :type effective_permission: FileSharedLinkEffectivePermissionField + :param is_password_enabled: Defines if the shared link requires a password to access the item. + :type is_password_enabled: bool + :param download_count: The number of times this item has been downloaded. + :type download_count: int + :param preview_count: The number of times this item has been previewed. + :type preview_count: int + :param download_url: A URL that can be used to download the file. This URL can be used in + a browser to download the file. This URL includes the file + extension so that the file will be saved with the right file type. + + This property will be `null` for folders., defaults to None + :type download_url: Optional[str], optional + :param vanity_url: The "Custom URL" that can also be used to preview the item on Box. Custom + URLs can only be created or modified in the Box Web application., defaults to None + :type vanity_url: Optional[str], optional + :param vanity_name: The custom name of a shared link, as used in the `vanity_url` field., defaults to None + :type vanity_name: Optional[str], optional + :param access: The access level for this shared link. + + * `open` - provides access to this item to anyone with this link + * `company` - only provides access to this item to people the same company + * `collaborators` - only provides access to this item to people who are + collaborators on this item + + If this field is omitted when creating the shared link, the access level + will be set to the default access level specified by the enterprise admin., defaults to None + :type access: Optional[FileSharedLinkAccessField], optional + :param unshared_at: The date and time when this link will be unshared. This field can only be + set by users with paid accounts., defaults to None + :type unshared_at: Optional[DateTime], optional + :param permissions: Defines if this link allows a user to preview, edit, and download an item. + These permissions refer to the shared link only and + do not supersede permissions applied to the item itself., defaults to None + :type permissions: Optional[FileSharedLinkPermissionsField], optional + """ + super().__init__(**kwargs) + self.url = url + self.effective_access = effective_access + self.effective_permission = effective_permission + self.is_password_enabled = is_password_enabled + self.download_count = download_count + self.preview_count = preview_count + self.download_url = download_url + self.vanity_url = vanity_url + self.vanity_name = vanity_name + self.access = access + self.unshared_at = unshared_at + self.permissions = permissions + + +class FileItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class File(FileMini): + def __init__( + self, + id: str, + *, + description: Optional[str] = None, + size: Optional[int] = None, + path_collection: Optional[FilePathCollectionField] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[FileSharedLinkField] = None, + parent: Optional[FolderMini] = None, + item_status: Optional[FileItemStatusField] = None, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + sha_1: Optional[str] = None, + file_version: Optional[FileVersionMini] = None, + etag: Optional[str] = None, + type: FileBaseTypeField = FileBaseTypeField.FILE, + **kwargs + ): + """ + :param id: The unique identifier that represent a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + :type id: str + :param description: The optional description of this file. + If the description exceeds 255 characters, the first 255 characters + are set as a file description and the rest of it is ignored., defaults to None + :type description: Optional[str], optional + :param size: The file size in bytes. Be careful parsing this integer as it can + get very large and cause an integer overflow., defaults to None + :type size: Optional[int], optional + :param created_at: The date and time when the file was created on Box., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The date and time when the file was last updated on Box., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: The time at which this file was put in the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: The time at which this file is expected to be purged + from the trash., defaults to None + :type purged_at: Optional[DateTime], optional + :param content_created_at: The date and time at which this file was originally + created, which might be before it was uploaded to Box., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this file was last updated, + which might be before it was uploaded to Box., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash + * `trashed` when the item has been moved to the trash but not deleted + * `deleted` when the item has been permanently deleted., defaults to None + :type item_status: Optional[FileItemStatusField], optional + :param name: The name of the file., defaults to None + :type name: Optional[str], optional + :param sha_1: The SHA1 hash of the file. This can be used to compare the contents + of a file on Box with a local file., defaults to None + :type sha_1: Optional[str], optional + :param etag: The HTTP `etag` of this file. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the file if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `file`., defaults to FileBaseTypeField.FILE + :type type: FileBaseTypeField, optional + """ + super().__init__( + id=id, + sequence_id=sequence_id, + name=name, + sha_1=sha_1, + file_version=file_version, + etag=etag, + type=type, + **kwargs + ) + self.description = description + self.size = size + self.path_collection = path_collection + self.created_at = created_at + self.modified_at = modified_at + self.trashed_at = trashed_at + self.purged_at = purged_at + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + self.created_by = created_by + self.modified_by = modified_by + self.owned_by = owned_by + self.shared_link = shared_link + self.parent = parent + self.item_status = item_status diff --git a/box_sdk_gen/schemas/file_base.py b/box_sdk_gen/schemas/file_base.py new file mode 100644 index 000000000..aa2c63a0f --- /dev/null +++ b/box_sdk_gen/schemas/file_base.py @@ -0,0 +1,44 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileBaseTypeField(str, Enum): + FILE = 'file' + + +class FileBase(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + id: str, + *, + etag: Optional[str] = None, + type: FileBaseTypeField = FileBaseTypeField.FILE, + **kwargs + ): + """ + :param id: The unique identifier that represent a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + :type id: str + :param etag: The HTTP `etag` of this file. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the file if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `file`., defaults to FileBaseTypeField.FILE + :type type: FileBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.etag = etag + self.type = type diff --git a/box_sdk_gen/schemas/file_conflict.py b/box_sdk_gen/schemas/file_conflict.py new file mode 100644 index 000000000..5e88917fe --- /dev/null +++ b/box_sdk_gen/schemas/file_conflict.py @@ -0,0 +1,57 @@ +from typing import Optional + +from box_sdk_gen.schemas.file_base import FileBaseTypeField + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileConflict(FileMini): + def __init__( + self, + id: str, + *, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + sha_1: Optional[str] = None, + file_version: Optional[FileVersionMini] = None, + etag: Optional[str] = None, + type: FileBaseTypeField = FileBaseTypeField.FILE, + **kwargs + ): + """ + :param id: The unique identifier that represent a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + :type id: str + :param name: The name of the file., defaults to None + :type name: Optional[str], optional + :param sha_1: The SHA1 hash of the file. This can be used to compare the contents + of a file on Box with a local file., defaults to None + :type sha_1: Optional[str], optional + :param etag: The HTTP `etag` of this file. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the file if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `file`., defaults to FileBaseTypeField.FILE + :type type: FileBaseTypeField, optional + """ + super().__init__( + id=id, + sequence_id=sequence_id, + name=name, + sha_1=sha_1, + file_version=file_version, + etag=etag, + type=type, + **kwargs + ) diff --git a/box_sdk_gen/schemas/file_full.py b/box_sdk_gen/schemas/file_full.py new file mode 100644 index 000000000..615fac845 --- /dev/null +++ b/box_sdk_gen/schemas/file_full.py @@ -0,0 +1,577 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.file_base import FileBaseTypeField + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.file import FilePathCollectionField + +from box_sdk_gen.schemas.file import FileSharedLinkField + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.file import FileItemStatusField + +from box_sdk_gen.schemas.file import File + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.resource_scope import ResourceScope + +from box_sdk_gen.schemas.metadata_full import MetadataFull + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FileFullPermissionsField(BaseObject): + def __init__( + self, + can_delete: bool, + can_download: bool, + can_invite_collaborator: bool, + can_rename: bool, + can_set_share_access: bool, + can_share: bool, + can_annotate: bool, + can_comment: bool, + can_preview: bool, + can_upload: bool, + can_view_annotations_all: bool, + can_view_annotations_self: bool, + **kwargs + ): + """ + :param can_delete: Specifies if the current user can delete this item. + :type can_delete: bool + :param can_download: Specifies if the current user can download this item. + :type can_download: bool + :param can_invite_collaborator: Specifies if the current user can invite new + users to collaborate on this item, and if the user can + update the role of a user already collaborated on this + item. + :type can_invite_collaborator: bool + :param can_rename: Specifies if the user can rename this item. + :type can_rename: bool + :param can_set_share_access: Specifies if the user can change the access level of an + existing shared link on this item. + :type can_set_share_access: bool + :param can_share: Specifies if the user can create a shared link for this item. + :type can_share: bool + :param can_annotate: Specifies if the user can place annotations on this file. + :type can_annotate: bool + :param can_comment: Specifies if the user can place comments on this file. + :type can_comment: bool + :param can_preview: Specifies if the user can preview this file. + :type can_preview: bool + :param can_upload: Specifies if the user can upload a new version of this file. + :type can_upload: bool + :param can_view_annotations_all: Specifies if the user view all annotations placed on this file. + :type can_view_annotations_all: bool + :param can_view_annotations_self: Specifies if the user view annotations placed by themselves + on this file. + :type can_view_annotations_self: bool + """ + super().__init__(**kwargs) + self.can_delete = can_delete + self.can_download = can_download + self.can_invite_collaborator = can_invite_collaborator + self.can_rename = can_rename + self.can_set_share_access = can_set_share_access + self.can_share = can_share + self.can_annotate = can_annotate + self.can_comment = can_comment + self.can_preview = can_preview + self.can_upload = can_upload + self.can_view_annotations_all = can_view_annotations_all + self.can_view_annotations_self = can_view_annotations_self + + +class FileFullLockTypeField(str, Enum): + LOCK = 'lock' + + +class FileFullLockAppTypeField(str, Enum): + GSUITE = 'gsuite' + OFFICE_WOPI = 'office_wopi' + OFFICE_WOPIPLUS = 'office_wopiplus' + OTHER = 'other' + + +class FileFullLockField(BaseObject): + _discriminator = 'type', {'lock'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[FileFullLockTypeField] = None, + created_by: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + expired_at: Optional[DateTime] = None, + is_download_prevented: Optional[bool] = None, + app_type: Optional[FileFullLockAppTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this lock., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `lock`., defaults to None + :type type: Optional[FileFullLockTypeField], optional + :param created_at: The time this lock was created at., defaults to None + :type created_at: Optional[DateTime], optional + :param expired_at: The time this lock is to expire at, which might be in the past., defaults to None + :type expired_at: Optional[DateTime], optional + :param is_download_prevented: Whether or not the file can be downloaded while locked., defaults to None + :type is_download_prevented: Optional[bool], optional + :param app_type: If the lock is managed by an application rather than a user, this + field identifies the type of the application that holds the lock. + This is an open enum and may be extended with additional values in + the future., defaults to None + :type app_type: Optional[FileFullLockAppTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.created_by = created_by + self.created_at = created_at + self.expired_at = expired_at + self.is_download_prevented = is_download_prevented + self.app_type = app_type + + +class FileFullExpiringEmbedLinkTokenTypeField(str, Enum): + BEARER = 'bearer' + + +class FileFullExpiringEmbedLinkField(BaseObject): + def __init__( + self, + *, + access_token: Optional[str] = None, + expires_in: Optional[int] = None, + token_type: Optional[FileFullExpiringEmbedLinkTokenTypeField] = None, + restricted_to: Optional[List[ResourceScope]] = None, + url: Optional[str] = None, + **kwargs + ): + """ + :param access_token: The requested access token., defaults to None + :type access_token: Optional[str], optional + :param expires_in: The time in seconds by which this token will expire., defaults to None + :type expires_in: Optional[int], optional + :param token_type: The type of access token returned., defaults to None + :type token_type: Optional[FileFullExpiringEmbedLinkTokenTypeField], optional + :param restricted_to: The permissions that this access token permits, + providing a list of resources (files, folders, etc) + and the scopes permitted for each of those resources., defaults to None + :type restricted_to: Optional[List[ResourceScope]], optional + :param url: The actual expiring embed URL for this file, constructed + from the file ID and access tokens specified in this object., defaults to None + :type url: Optional[str], optional + """ + super().__init__(**kwargs) + self.access_token = access_token + self.expires_in = expires_in + self.token_type = token_type + self.restricted_to = restricted_to + self.url = url + + +class FileFullWatermarkInfoField(BaseObject): + def __init__(self, *, is_watermarked: Optional[bool] = None, **kwargs): + """ + :param is_watermarked: Specifies if this item has a watermark applied., defaults to None + :type is_watermarked: Optional[bool], optional + """ + super().__init__(**kwargs) + self.is_watermarked = is_watermarked + + +class FileFullAllowedInviteeRolesField(str, Enum): + EDITOR = 'editor' + VIEWER = 'viewer' + PREVIEWER = 'previewer' + UPLOADER = 'uploader' + PREVIEWER_UPLOADER = 'previewer uploader' + VIEWER_UPLOADER = 'viewer uploader' + CO_OWNER = 'co-owner' + + +class FileFullMetadataField(BaseObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.extra_data = kwargs + + +class FileFullRepresentationsEntriesContentField(BaseObject): + def __init__(self, *, url_template: Optional[str] = None, **kwargs): + """ + :param url_template: The download URL that can be used to fetch the representation. + Make sure to make an authenticated API call to this endpoint. + + This URL is a template and will require the `{+asset_path}` to + be replaced by a path. In general, for unpaged representations + it can be replaced by an empty string. + + For paged representations, replace the `{+asset_path}` with the + page to request plus the extension for the file, for example + `1.pdf`. + + When requesting the download URL the following additional + query params can be passed along. + + * `set_content_disposition_type` - Sets the + `Content-Disposition` header in the API response with the + specified disposition type of either `inline` or `attachment`. + If not supplied, the `Content-Disposition` header is not + included in the response. + + * `set_content_disposition_filename` - Allows the application to + define the representation's file name used in the + `Content-Disposition` header. If not defined, the filename + is derived from the source file name in Box combined with the + extension of the representation., defaults to None + :type url_template: Optional[str], optional + """ + super().__init__(**kwargs) + self.url_template = url_template + + +class FileFullRepresentationsEntriesInfoField(BaseObject): + def __init__(self, *, url: Optional[str] = None, **kwargs): + """ + :param url: The API URL that can be used to get more info on this file + representation. Make sure to make an authenticated API call + to this endpoint., defaults to None + :type url: Optional[str], optional + """ + super().__init__(**kwargs) + self.url = url + + +class FileFullRepresentationsEntriesPropertiesField(BaseObject): + def __init__( + self, + *, + dimensions: Optional[str] = None, + paged: Optional[str] = None, + thumb: Optional[str] = None, + **kwargs + ): + """ + :param dimensions: The width by height size of this representation in pixels., defaults to None + :type dimensions: Optional[str], optional + :param paged: Indicates if the representation is build up out of multiple + pages., defaults to None + :type paged: Optional[str], optional + :param thumb: Indicates if the representation can be used as a thumbnail of + the file., defaults to None + :type thumb: Optional[str], optional + """ + super().__init__(**kwargs) + self.dimensions = dimensions + self.paged = paged + self.thumb = thumb + + +class FileFullRepresentationsEntriesStatusStateField(str, Enum): + SUCCESS = 'success' + VIEWABLE = 'viewable' + PENDING = 'pending' + NONE = 'none' + + +class FileFullRepresentationsEntriesStatusField(BaseObject): + def __init__( + self, + *, + state: Optional[FileFullRepresentationsEntriesStatusStateField] = None, + **kwargs + ): + """ + :param state: The status of the representation. + + * `success` defines the representation as ready to be viewed. + * `viewable` defines a video to be ready for viewing. + * `pending` defines the representation as to be generated. Retry + this endpoint to re-check the status. + * `none` defines that the representation will be created when + requested. Request the URL defined in the `info` object to + trigger this generation., defaults to None + :type state: Optional[FileFullRepresentationsEntriesStatusStateField], optional + """ + super().__init__(**kwargs) + self.state = state + + +class FileFullRepresentationsEntriesField(BaseObject): + def __init__( + self, + *, + content: Optional[FileFullRepresentationsEntriesContentField] = None, + info: Optional[FileFullRepresentationsEntriesInfoField] = None, + properties: Optional[FileFullRepresentationsEntriesPropertiesField] = None, + representation: Optional[str] = None, + status: Optional[FileFullRepresentationsEntriesStatusField] = None, + **kwargs + ): + """ + :param content: An object containing the URL that can be used to actually fetch + the representation., defaults to None + :type content: Optional[FileFullRepresentationsEntriesContentField], optional + :param info: An object containing the URL that can be used to fetch more info + on this representation., defaults to None + :type info: Optional[FileFullRepresentationsEntriesInfoField], optional + :param properties: An object containing the size and type of this presentation., defaults to None + :type properties: Optional[FileFullRepresentationsEntriesPropertiesField], optional + :param representation: Indicates the file type of the returned representation., defaults to None + :type representation: Optional[str], optional + :param status: An object containing the status of this representation., defaults to None + :type status: Optional[FileFullRepresentationsEntriesStatusField], optional + """ + super().__init__(**kwargs) + self.content = content + self.info = info + self.properties = properties + self.representation = representation + self.status = status + + +class FileFullRepresentationsField(BaseObject): + def __init__( + self, + *, + entries: Optional[List[FileFullRepresentationsEntriesField]] = None, + **kwargs + ): + """ + :param entries: A list of files., defaults to None + :type entries: Optional[List[FileFullRepresentationsEntriesField]], optional + """ + super().__init__(**kwargs) + self.entries = entries + + +class FileFullClassificationField(BaseObject): + def __init__( + self, + *, + name: Optional[str] = None, + definition: Optional[str] = None, + color: Optional[str] = None, + **kwargs + ): + """ + :param name: The name of the classification., defaults to None + :type name: Optional[str], optional + :param definition: An explanation of the meaning of this classification., defaults to None + :type definition: Optional[str], optional + :param color: The color that is used to display the + classification label in a user-interface. Colors are defined by the admin + or co-admin who created the classification in the Box web app., defaults to None + :type color: Optional[str], optional + """ + super().__init__(**kwargs) + self.name = name + self.definition = definition + self.color = color + + +class FileFullSharedLinkPermissionOptionsField(str, Enum): + CAN_PREVIEW = 'can_preview' + CAN_DOWNLOAD = 'can_download' + CAN_EDIT = 'can_edit' + + +class FileFull(File): + def __init__( + self, + id: str, + *, + version_number: Optional[str] = None, + comment_count: Optional[int] = None, + permissions: Optional[FileFullPermissionsField] = None, + tags: Optional[List[str]] = None, + lock: Optional[FileFullLockField] = None, + extension: Optional[str] = None, + is_package: Optional[bool] = None, + expiring_embed_link: Optional[FileFullExpiringEmbedLinkField] = None, + watermark_info: Optional[FileFullWatermarkInfoField] = None, + is_accessible_via_shared_link: Optional[bool] = None, + allowed_invitee_roles: Optional[List[FileFullAllowedInviteeRolesField]] = None, + is_externally_owned: Optional[bool] = None, + has_collaborations: Optional[bool] = None, + metadata: Optional[FileFullMetadataField] = None, + expires_at: Optional[DateTime] = None, + representations: Optional[FileFullRepresentationsField] = None, + classification: Optional[FileFullClassificationField] = None, + uploader_display_name: Optional[str] = None, + disposition_at: Optional[DateTime] = None, + shared_link_permission_options: Optional[ + List[FileFullSharedLinkPermissionOptionsField] + ] = None, + is_associated_with_app_item: Optional[bool] = None, + description: Optional[str] = None, + size: Optional[int] = None, + path_collection: Optional[FilePathCollectionField] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[FileSharedLinkField] = None, + parent: Optional[FolderMini] = None, + item_status: Optional[FileItemStatusField] = None, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + sha_1: Optional[str] = None, + file_version: Optional[FileVersionMini] = None, + etag: Optional[str] = None, + type: FileBaseTypeField = FileBaseTypeField.FILE, + **kwargs + ): + """ + :param id: The unique identifier that represent a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + :type id: str + :param version_number: The version number of this file., defaults to None + :type version_number: Optional[str], optional + :param comment_count: The number of comments on this file., defaults to None + :type comment_count: Optional[int], optional + :param extension: Indicates the (optional) file extension for this file. By default, + this is set to an empty string., defaults to None + :type extension: Optional[str], optional + :param is_package: Indicates if the file is a package. Packages are commonly used + by Mac Applications and can include iWork files., defaults to None + :type is_package: Optional[bool], optional + :param is_accessible_via_shared_link: Specifies if the file can be accessed + via the direct shared link or a shared link + to a parent folder., defaults to None + :type is_accessible_via_shared_link: Optional[bool], optional + :param allowed_invitee_roles: A list of the types of roles that user can be invited at + when sharing this file., defaults to None + :type allowed_invitee_roles: Optional[List[FileFullAllowedInviteeRolesField]], optional + :param is_externally_owned: Specifies if this file is owned by a user outside of the + authenticated enterprise., defaults to None + :type is_externally_owned: Optional[bool], optional + :param has_collaborations: Specifies if this file has any other collaborators., defaults to None + :type has_collaborations: Optional[bool], optional + :param expires_at: When the file will automatically be deleted., defaults to None + :type expires_at: Optional[DateTime], optional + :param disposition_at: The retention expiration timestamp for the given file., defaults to None + :type disposition_at: Optional[DateTime], optional + :param shared_link_permission_options: A list of the types of roles that user can be invited at + when sharing this file., defaults to None + :type shared_link_permission_options: Optional[List[FileFullSharedLinkPermissionOptionsField]], optional + :param is_associated_with_app_item: This field will return true if the file or any ancestor of the file + is associated with at least one app item. Note that this will return + true even if the context user does not have access to the app item(s) + associated with the file., defaults to None + :type is_associated_with_app_item: Optional[bool], optional + :param description: The optional description of this file. + If the description exceeds 255 characters, the first 255 characters + are set as a file description and the rest of it is ignored., defaults to None + :type description: Optional[str], optional + :param size: The file size in bytes. Be careful parsing this integer as it can + get very large and cause an integer overflow., defaults to None + :type size: Optional[int], optional + :param created_at: The date and time when the file was created on Box., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The date and time when the file was last updated on Box., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: The time at which this file was put in the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: The time at which this file is expected to be purged + from the trash., defaults to None + :type purged_at: Optional[DateTime], optional + :param content_created_at: The date and time at which this file was originally + created, which might be before it was uploaded to Box., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this file was last updated, + which might be before it was uploaded to Box., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash + * `trashed` when the item has been moved to the trash but not deleted + * `deleted` when the item has been permanently deleted., defaults to None + :type item_status: Optional[FileItemStatusField], optional + :param name: The name of the file., defaults to None + :type name: Optional[str], optional + :param sha_1: The SHA1 hash of the file. This can be used to compare the contents + of a file on Box with a local file., defaults to None + :type sha_1: Optional[str], optional + :param etag: The HTTP `etag` of this file. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the file if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `file`., defaults to FileBaseTypeField.FILE + :type type: FileBaseTypeField, optional + """ + super().__init__( + id=id, + description=description, + size=size, + path_collection=path_collection, + created_at=created_at, + modified_at=modified_at, + trashed_at=trashed_at, + purged_at=purged_at, + content_created_at=content_created_at, + content_modified_at=content_modified_at, + created_by=created_by, + modified_by=modified_by, + owned_by=owned_by, + shared_link=shared_link, + parent=parent, + item_status=item_status, + sequence_id=sequence_id, + name=name, + sha_1=sha_1, + file_version=file_version, + etag=etag, + type=type, + **kwargs + ) + self.version_number = version_number + self.comment_count = comment_count + self.permissions = permissions + self.tags = tags + self.lock = lock + self.extension = extension + self.is_package = is_package + self.expiring_embed_link = expiring_embed_link + self.watermark_info = watermark_info + self.is_accessible_via_shared_link = is_accessible_via_shared_link + self.allowed_invitee_roles = allowed_invitee_roles + self.is_externally_owned = is_externally_owned + self.has_collaborations = has_collaborations + self.metadata = metadata + self.expires_at = expires_at + self.representations = representations + self.classification = classification + self.uploader_display_name = uploader_display_name + self.disposition_at = disposition_at + self.shared_link_permission_options = shared_link_permission_options + self.is_associated_with_app_item = is_associated_with_app_item diff --git a/box_sdk_gen/schemas/file_mini.py b/box_sdk_gen/schemas/file_mini.py new file mode 100644 index 000000000..c8b72f4e7 --- /dev/null +++ b/box_sdk_gen/schemas/file_mini.py @@ -0,0 +1,61 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.schemas.file_base import FileBaseTypeField + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileMini(FileBase): + _fields_to_json_mapping: Dict[str, str] = { + 'sha_1': 'sha1', + **FileBase._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'sha1': 'sha_1', + **FileBase._json_to_fields_mapping, + } + + def __init__( + self, + id: str, + *, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + sha_1: Optional[str] = None, + file_version: Optional[FileVersionMini] = None, + etag: Optional[str] = None, + type: FileBaseTypeField = FileBaseTypeField.FILE, + **kwargs + ): + """ + :param id: The unique identifier that represent a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + :type id: str + :param name: The name of the file., defaults to None + :type name: Optional[str], optional + :param sha_1: The SHA1 hash of the file. This can be used to compare the contents + of a file on Box with a local file., defaults to None + :type sha_1: Optional[str], optional + :param etag: The HTTP `etag` of this file. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the file if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `file`., defaults to FileBaseTypeField.FILE + :type type: FileBaseTypeField, optional + """ + super().__init__(id=id, etag=etag, type=type, **kwargs) + self.sequence_id = sequence_id + self.name = name + self.sha_1 = sha_1 + self.file_version = file_version diff --git a/box_sdk_gen/schemas/file_request.py b/box_sdk_gen/schemas/file_request.py new file mode 100644 index 000000000..ea17ff10a --- /dev/null +++ b/box_sdk_gen/schemas/file_request.py @@ -0,0 +1,129 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FileRequestTypeField(str, Enum): + FILE_REQUEST = 'file_request' + + +class FileRequestStatusField(str, Enum): + ACTIVE = 'active' + INACTIVE = 'inactive' + + +class FileRequest(BaseObject): + _discriminator = 'type', {'file_request'} + + def __init__( + self, + id: str, + folder: FolderMini, + created_at: DateTime, + updated_at: DateTime, + *, + type: FileRequestTypeField = FileRequestTypeField.FILE_REQUEST, + title: Optional[str] = None, + description: Optional[str] = None, + status: Optional[FileRequestStatusField] = None, + is_email_required: Optional[bool] = None, + is_description_required: Optional[bool] = None, + expires_at: Optional[DateTime] = None, + url: Optional[str] = None, + etag: Optional[str] = None, + created_by: Optional[UserMini] = None, + updated_by: Optional[UserMini] = None, + **kwargs + ): + """ + :param id: The unique identifier for this file request. + :type id: str + :param created_at: The date and time when the file request was created. + :type created_at: DateTime + :param updated_at: The date and time when the file request was last updated. + :type updated_at: DateTime + :param type: The value will always be `file_request`., defaults to FileRequestTypeField.FILE_REQUEST + :type type: FileRequestTypeField, optional + :param title: The title of file request. This is shown + in the Box UI to users uploading files. + + This defaults to title of the file request that was + copied to create this file request., defaults to None + :type title: Optional[str], optional + :param description: The optional description of this file request. This is + shown in the Box UI to users uploading files. + + This defaults to description of the file request that was + copied to create this file request., defaults to None + :type description: Optional[str], optional + :param status: The status of the file request. This defaults + to `active`. + + When the status is set to `inactive`, the file request + will no longer accept new submissions, and any visitor + to the file request URL will receive a `HTTP 404` status + code. + + This defaults to status of file request that was + copied to create this file request., defaults to None + :type status: Optional[FileRequestStatusField], optional + :param is_email_required: Whether a file request submitter is required to provide + their email address. + + When this setting is set to true, the Box UI will show + an email field on the file request form. + + This defaults to setting of file request that was + copied to create this file request., defaults to None + :type is_email_required: Optional[bool], optional + :param is_description_required: Whether a file request submitter is required to provide + a description of the files they are submitting. + + When this setting is set to true, the Box UI will show + a description field on the file request form. + + This defaults to setting of file request that was + copied to create this file request., defaults to None + :type is_description_required: Optional[bool], optional + :param expires_at: The date after which a file request will no longer accept new + submissions. + + After this date, the `status` will automatically be set to + `inactive`., defaults to None + :type expires_at: Optional[DateTime], optional + :param url: The generated URL for this file request. This URL can be shared + with users to let them upload files to the associated folder., defaults to None + :type url: Optional[str], optional + :param etag: The HTTP `etag` of this file. This can be used in combination with + the `If-Match` header when updating a file request. By providing that + header, a change will only be performed on the file request if the `etag` + on the file request still matches the `etag` provided in the `If-Match` + header., defaults to None + :type etag: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.folder = folder + self.created_at = created_at + self.updated_at = updated_at + self.type = type + self.title = title + self.description = description + self.status = status + self.is_email_required = is_email_required + self.is_description_required = is_description_required + self.expires_at = expires_at + self.url = url + self.etag = etag + self.created_by = created_by + self.updated_by = updated_by diff --git a/box_sdk_gen/schemas/file_request_copy_request.py b/box_sdk_gen/schemas/file_request_copy_request.py new file mode 100644 index 000000000..93fc86e18 --- /dev/null +++ b/box_sdk_gen/schemas/file_request_copy_request.py @@ -0,0 +1,113 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_request_update_request import ( + FileRequestUpdateRequestStatusField, +) + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.file_request_update_request import FileRequestUpdateRequest + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileRequestCopyRequestFolderTypeField(str, Enum): + FOLDER = 'folder' + + +class FileRequestCopyRequestFolderField(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + type: Optional[FileRequestCopyRequestFolderTypeField] = None, + **kwargs + ): + """ + :param id: The ID of the folder to associate the new + file request to. + :type id: str + :param type: The value will always be `folder`., defaults to None + :type type: Optional[FileRequestCopyRequestFolderTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class FileRequestCopyRequest(FileRequestUpdateRequest): + def __init__( + self, + folder: FileRequestCopyRequestFolderField, + *, + title: Optional[str] = None, + description: Optional[str] = None, + status: Optional[FileRequestUpdateRequestStatusField] = None, + is_email_required: Optional[bool] = None, + is_description_required: Optional[bool] = None, + expires_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param folder: The folder to associate the new file request to. + :type folder: FileRequestCopyRequestFolderField + :param title: An optional new title for the file request. This can be + used to change the title of the file request. + + This will default to the value on the existing file request., defaults to None + :type title: Optional[str], optional + :param description: An optional new description for the file request. This can be + used to change the description of the file request. + + This will default to the value on the existing file request., defaults to None + :type description: Optional[str], optional + :param status: An optional new status of the file request. + + When the status is set to `inactive`, the file request + will no longer accept new submissions, and any visitor + to the file request URL will receive a `HTTP 404` status + code. + + This will default to the value on the existing file request., defaults to None + :type status: Optional[FileRequestUpdateRequestStatusField], optional + :param is_email_required: Whether a file request submitter is required to provide + their email address. + + When this setting is set to true, the Box UI will show + an email field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_email_required: Optional[bool], optional + :param is_description_required: Whether a file request submitter is required to provide + a description of the files they are submitting. + + When this setting is set to true, the Box UI will show + a description field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_description_required: Optional[bool], optional + :param expires_at: The date after which a file request will no longer accept new + submissions. + + After this date, the `status` will automatically be set to + `inactive`. + + This will default to the value on the existing file request., defaults to None + :type expires_at: Optional[DateTime], optional + """ + super().__init__( + title=title, + description=description, + status=status, + is_email_required=is_email_required, + is_description_required=is_description_required, + expires_at=expires_at, + **kwargs + ) + self.folder = folder diff --git a/box_sdk_gen/schemas/file_request_update_request.py b/box_sdk_gen/schemas/file_request_update_request.py new file mode 100644 index 000000000..e496d6124 --- /dev/null +++ b/box_sdk_gen/schemas/file_request_update_request.py @@ -0,0 +1,80 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FileRequestUpdateRequestStatusField(str, Enum): + ACTIVE = 'active' + INACTIVE = 'inactive' + + +class FileRequestUpdateRequest(BaseObject): + def __init__( + self, + *, + title: Optional[str] = None, + description: Optional[str] = None, + status: Optional[FileRequestUpdateRequestStatusField] = None, + is_email_required: Optional[bool] = None, + is_description_required: Optional[bool] = None, + expires_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param title: An optional new title for the file request. This can be + used to change the title of the file request. + + This will default to the value on the existing file request., defaults to None + :type title: Optional[str], optional + :param description: An optional new description for the file request. This can be + used to change the description of the file request. + + This will default to the value on the existing file request., defaults to None + :type description: Optional[str], optional + :param status: An optional new status of the file request. + + When the status is set to `inactive`, the file request + will no longer accept new submissions, and any visitor + to the file request URL will receive a `HTTP 404` status + code. + + This will default to the value on the existing file request., defaults to None + :type status: Optional[FileRequestUpdateRequestStatusField], optional + :param is_email_required: Whether a file request submitter is required to provide + their email address. + + When this setting is set to true, the Box UI will show + an email field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_email_required: Optional[bool], optional + :param is_description_required: Whether a file request submitter is required to provide + a description of the files they are submitting. + + When this setting is set to true, the Box UI will show + a description field on the file request form. + + This will default to the value on the existing file request., defaults to None + :type is_description_required: Optional[bool], optional + :param expires_at: The date after which a file request will no longer accept new + submissions. + + After this date, the `status` will automatically be set to + `inactive`. + + This will default to the value on the existing file request., defaults to None + :type expires_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.title = title + self.description = description + self.status = status + self.is_email_required = is_email_required + self.is_description_required = is_description_required + self.expires_at = expires_at diff --git a/box_sdk_gen/schemas/file_version.py b/box_sdk_gen/schemas/file_version.py new file mode 100644 index 000000000..3b213fb07 --- /dev/null +++ b/box_sdk_gen/schemas/file_version.py @@ -0,0 +1,69 @@ +from typing import Optional + +from box_sdk_gen.schemas.file_version_base import FileVersionBaseTypeField + +from box_sdk_gen.schemas.file_version_base import FileVersionBase + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FileVersion(FileVersionMini): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + size: Optional[int] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + modified_by: Optional[UserMini] = None, + trashed_at: Optional[DateTime] = None, + trashed_by: Optional[UserMini] = None, + restored_at: Optional[DateTime] = None, + restored_by: Optional[UserMini] = None, + purged_at: Optional[DateTime] = None, + uploader_display_name: Optional[str] = None, + sha_1: Optional[str] = None, + type: FileVersionBaseTypeField = FileVersionBaseTypeField.FILE_VERSION, + **kwargs + ): + """ + :param id: The unique identifier that represent a file version. + :type id: str + :param name: The name of the file version., defaults to None + :type name: Optional[str], optional + :param size: Size of the file version in bytes., defaults to None + :type size: Optional[int], optional + :param created_at: When the file version object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the file version object was last updated., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: When the file version object was trashed., defaults to None + :type trashed_at: Optional[DateTime], optional + :param restored_at: When the file version was restored from the trash., defaults to None + :type restored_at: Optional[DateTime], optional + :param purged_at: When the file version object will be permanently deleted., defaults to None + :type purged_at: Optional[DateTime], optional + :param sha_1: The SHA1 hash of this version of the file., defaults to None + :type sha_1: Optional[str], optional + :param type: The value will always be `file_version`., defaults to FileVersionBaseTypeField.FILE_VERSION + :type type: FileVersionBaseTypeField, optional + """ + super().__init__(id=id, sha_1=sha_1, type=type, **kwargs) + self.name = name + self.size = size + self.created_at = created_at + self.modified_at = modified_at + self.modified_by = modified_by + self.trashed_at = trashed_at + self.trashed_by = trashed_by + self.restored_at = restored_at + self.restored_by = restored_by + self.purged_at = purged_at + self.uploader_display_name = uploader_display_name diff --git a/box_sdk_gen/schemas/file_version_base.py b/box_sdk_gen/schemas/file_version_base.py new file mode 100644 index 000000000..d6fba5be6 --- /dev/null +++ b/box_sdk_gen/schemas/file_version_base.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileVersionBaseTypeField(str, Enum): + FILE_VERSION = 'file_version' + + +class FileVersionBase(BaseObject): + _discriminator = 'type', {'file_version'} + + def __init__( + self, + id: str, + *, + type: FileVersionBaseTypeField = FileVersionBaseTypeField.FILE_VERSION, + **kwargs + ): + """ + :param id: The unique identifier that represent a file version. + :type id: str + :param type: The value will always be `file_version`., defaults to FileVersionBaseTypeField.FILE_VERSION + :type type: FileVersionBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/file_version_full.py b/box_sdk_gen/schemas/file_version_full.py new file mode 100644 index 000000000..7d778bd3a --- /dev/null +++ b/box_sdk_gen/schemas/file_version_full.py @@ -0,0 +1,80 @@ +from typing import Optional + +from box_sdk_gen.schemas.file_version_base import FileVersionBaseTypeField + +from box_sdk_gen.schemas.file_version_base import FileVersionBase + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.file_version import FileVersion + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileVersionFull(FileVersion): + def __init__( + self, + id: str, + *, + version_number: Optional[str] = None, + name: Optional[str] = None, + size: Optional[int] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + modified_by: Optional[UserMini] = None, + trashed_at: Optional[DateTime] = None, + trashed_by: Optional[UserMini] = None, + restored_at: Optional[DateTime] = None, + restored_by: Optional[UserMini] = None, + purged_at: Optional[DateTime] = None, + uploader_display_name: Optional[str] = None, + sha_1: Optional[str] = None, + type: FileVersionBaseTypeField = FileVersionBaseTypeField.FILE_VERSION, + **kwargs + ): + """ + :param id: The unique identifier that represent a file version. + :type id: str + :param version_number: The version number of this file version., defaults to None + :type version_number: Optional[str], optional + :param name: The name of the file version., defaults to None + :type name: Optional[str], optional + :param size: Size of the file version in bytes., defaults to None + :type size: Optional[int], optional + :param created_at: When the file version object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the file version object was last updated., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: When the file version object was trashed., defaults to None + :type trashed_at: Optional[DateTime], optional + :param restored_at: When the file version was restored from the trash., defaults to None + :type restored_at: Optional[DateTime], optional + :param purged_at: When the file version object will be permanently deleted., defaults to None + :type purged_at: Optional[DateTime], optional + :param sha_1: The SHA1 hash of this version of the file., defaults to None + :type sha_1: Optional[str], optional + :param type: The value will always be `file_version`., defaults to FileVersionBaseTypeField.FILE_VERSION + :type type: FileVersionBaseTypeField, optional + """ + super().__init__( + id=id, + name=name, + size=size, + created_at=created_at, + modified_at=modified_at, + modified_by=modified_by, + trashed_at=trashed_at, + trashed_by=trashed_by, + restored_at=restored_at, + restored_by=restored_by, + purged_at=purged_at, + uploader_display_name=uploader_display_name, + sha_1=sha_1, + type=type, + **kwargs + ) + self.version_number = version_number diff --git a/box_sdk_gen/schemas/file_version_legal_hold.py b/box_sdk_gen/schemas/file_version_legal_hold.py new file mode 100644 index 000000000..67da341f4 --- /dev/null +++ b/box_sdk_gen/schemas/file_version_legal_hold.py @@ -0,0 +1,55 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.legal_hold_policy_assignment import LegalHoldPolicyAssignment + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FileVersionLegalHoldTypeField(str, Enum): + FILE_VERSION_LEGAL_HOLD = 'file_version_legal_hold' + + +class FileVersionLegalHold(BaseObject): + _discriminator = 'type', {'file_version_legal_hold'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[FileVersionLegalHoldTypeField] = None, + file_version: Optional[FileVersionMini] = None, + file: Optional[FileMini] = None, + legal_hold_policy_assignments: Optional[List[LegalHoldPolicyAssignment]] = None, + deleted_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param id: The unique identifier for this file version legal hold., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `file_version_legal_hold`., defaults to None + :type type: Optional[FileVersionLegalHoldTypeField], optional + :param legal_hold_policy_assignments: List of assignments contributing to this Hold., defaults to None + :type legal_hold_policy_assignments: Optional[List[LegalHoldPolicyAssignment]], optional + :param deleted_at: Time that this File-Version-Legal-Hold was + deleted., defaults to None + :type deleted_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.file_version = file_version + self.file = file + self.legal_hold_policy_assignments = legal_hold_policy_assignments + self.deleted_at = deleted_at diff --git a/box_sdk_gen/schemas/file_version_legal_holds.py b/box_sdk_gen/schemas/file_version_legal_holds.py new file mode 100644 index 000000000..fcf4c11dd --- /dev/null +++ b/box_sdk_gen/schemas/file_version_legal_holds.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_version_legal_hold import FileVersionLegalHold + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileVersionLegalHolds(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[FileVersionLegalHold]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of file version legal holds., defaults to None + :type entries: Optional[List[FileVersionLegalHold]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/file_version_mini.py b/box_sdk_gen/schemas/file_version_mini.py new file mode 100644 index 000000000..d4c03e4c9 --- /dev/null +++ b/box_sdk_gen/schemas/file_version_mini.py @@ -0,0 +1,39 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.schemas.file_version_base import FileVersionBaseTypeField + +from box_sdk_gen.schemas.file_version_base import FileVersionBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileVersionMini(FileVersionBase): + _fields_to_json_mapping: Dict[str, str] = { + 'sha_1': 'sha1', + **FileVersionBase._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'sha1': 'sha_1', + **FileVersionBase._json_to_fields_mapping, + } + + def __init__( + self, + id: str, + *, + sha_1: Optional[str] = None, + type: FileVersionBaseTypeField = FileVersionBaseTypeField.FILE_VERSION, + **kwargs + ): + """ + :param id: The unique identifier that represent a file version. + :type id: str + :param sha_1: The SHA1 hash of this version of the file., defaults to None + :type sha_1: Optional[str], optional + :param type: The value will always be `file_version`., defaults to FileVersionBaseTypeField.FILE_VERSION + :type type: FileVersionBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.sha_1 = sha_1 diff --git a/box_sdk_gen/schemas/file_version_retention.py b/box_sdk_gen/schemas/file_version_retention.py new file mode 100644 index 000000000..3321cc4f7 --- /dev/null +++ b/box_sdk_gen/schemas/file_version_retention.py @@ -0,0 +1,56 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.retention_policy_mini import RetentionPolicyMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FileVersionRetentionTypeField(str, Enum): + FILE_VERSION_RETENTION = 'file_version_retention' + + +class FileVersionRetention(BaseObject): + _discriminator = 'type', {'file_version_retention'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[FileVersionRetentionTypeField] = None, + file_version: Optional[FileVersionMini] = None, + file: Optional[FileMini] = None, + applied_at: Optional[DateTime] = None, + disposition_at: Optional[DateTime] = None, + winning_retention_policy: Optional[RetentionPolicyMini] = None, + **kwargs + ): + """ + :param id: The unique identifier for this file version retention., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `file_version_retention`., defaults to None + :type type: Optional[FileVersionRetentionTypeField], optional + :param applied_at: When this file version retention object was + created., defaults to None + :type applied_at: Optional[DateTime], optional + :param disposition_at: When the retention expires on this file + version retention., defaults to None + :type disposition_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.file_version = file_version + self.file = file + self.applied_at = applied_at + self.disposition_at = disposition_at + self.winning_retention_policy = winning_retention_policy diff --git a/box_sdk_gen/schemas/file_version_retentions.py b/box_sdk_gen/schemas/file_version_retentions.py new file mode 100644 index 000000000..e83034c38 --- /dev/null +++ b/box_sdk_gen/schemas/file_version_retentions.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_version_retention import FileVersionRetention + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileVersionRetentions(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[FileVersionRetention]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of file version retentions., defaults to None + :type entries: Optional[List[FileVersionRetention]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/file_versions.py b/box_sdk_gen/schemas/file_versions.py new file mode 100644 index 000000000..494f249cd --- /dev/null +++ b/box_sdk_gen/schemas/file_versions.py @@ -0,0 +1,80 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.file_version_full import FileVersionFull + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileVersionsOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class FileVersionsOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[FileVersionsOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[FileVersionsOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class FileVersions(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[FileVersionsOrderField]] = None, + entries: Optional[List[FileVersionFull]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[FileVersionsOrderField]], optional + :param entries: A list of file versions., defaults to None + :type entries: Optional[List[FileVersionFull]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/files.py b/box_sdk_gen/schemas/files.py new file mode 100644 index 000000000..428c1fcab --- /dev/null +++ b/box_sdk_gen/schemas/files.py @@ -0,0 +1,28 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.box.errors import BoxSDKError + + +class Files(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + entries: Optional[List[FileFull]] = None, + **kwargs + ): + """ + :param total_count: The number of files., defaults to None + :type total_count: Optional[int], optional + :param entries: A list of files., defaults to None + :type entries: Optional[List[FileFull]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries diff --git a/box_sdk_gen/schemas/files_on_hold.py b/box_sdk_gen/schemas/files_on_hold.py new file mode 100644 index 000000000..9918c0066 --- /dev/null +++ b/box_sdk_gen/schemas/files_on_hold.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class FilesOnHold(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[FileMini]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of files., defaults to None + :type entries: Optional[List[FileMini]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/files_under_retention.py b/box_sdk_gen/schemas/files_under_retention.py new file mode 100644 index 000000000..6b2211a75 --- /dev/null +++ b/box_sdk_gen/schemas/files_under_retention.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class FilesUnderRetention(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[FileMini]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of files., defaults to None + :type entries: Optional[List[FileMini]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/folder.py b/box_sdk_gen/schemas/folder.py new file mode 100644 index 000000000..a0330bec5 --- /dev/null +++ b/box_sdk_gen/schemas/folder.py @@ -0,0 +1,313 @@ +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.folder_base import FolderBaseTypeField + +from box_sdk_gen.schemas.folder_base import FolderBase + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.items import Items + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FolderPathCollectionField(BaseObject): + def __init__(self, total_count: int, entries: List[FolderMini], **kwargs): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: The parent folders for this item. + :type entries: List[FolderMini] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class FolderSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class FolderSharedLinkEffectiveAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class FolderSharedLinkEffectivePermissionField(str, Enum): + CAN_EDIT = 'can_edit' + CAN_DOWNLOAD = 'can_download' + CAN_PREVIEW = 'can_preview' + NO_ACCESS = 'no_access' + + +class FolderSharedLinkPermissionsField(BaseObject): + def __init__(self, can_download: bool, can_preview: bool, can_edit: bool, **kwargs): + """ + :param can_download: Defines if the shared link allows for the item to be downloaded. For + shared links on folders, this also applies to any items in the folder. + + This value can be set to `true` when the effective access level is + set to `open` or `company`, not `collaborators`. + :type can_download: bool + :param can_preview: Defines if the shared link allows for the item to be previewed. + + This value is always `true`. For shared links on folders this also + applies to any items in the folder. + :type can_preview: bool + :param can_edit: Defines if the shared link allows for the item to be edited. + + This value can only be `true` if `can_download` is also `true` and if + the item has a type of `file`. + :type can_edit: bool + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class FolderSharedLinkField(BaseObject): + def __init__( + self, + url: str, + effective_access: FolderSharedLinkEffectiveAccessField, + effective_permission: FolderSharedLinkEffectivePermissionField, + is_password_enabled: bool, + download_count: int, + preview_count: int, + *, + download_url: Optional[str] = None, + vanity_url: Optional[str] = None, + vanity_name: Optional[str] = None, + access: Optional[FolderSharedLinkAccessField] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[FolderSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param url: The URL that can be used to access the item on Box. + + This URL will display the item in Box's preview UI where the file + can be downloaded if allowed. + + This URL will continue to work even when a custom `vanity_url` + has been set for this shared link. + :type url: str + :param effective_access: The effective access level for the shared link. This can be a more + restrictive access level than the value in the `access` field when the + enterprise settings restrict the allowed access levels. + :type effective_access: FolderSharedLinkEffectiveAccessField + :param effective_permission: The effective permissions for this shared link. + These result in the more restrictive combination of + the share link permissions and the item permissions set + by the administrator, the owner, and any ancestor item + such as a folder. + :type effective_permission: FolderSharedLinkEffectivePermissionField + :param is_password_enabled: Defines if the shared link requires a password to access the item. + :type is_password_enabled: bool + :param download_count: The number of times this item has been downloaded. + :type download_count: int + :param preview_count: The number of times this item has been previewed. + :type preview_count: int + :param download_url: A URL that can be used to download the file. This URL can be used in + a browser to download the file. This URL includes the file + extension so that the file will be saved with the right file type. + + This property will be `null` for folders., defaults to None + :type download_url: Optional[str], optional + :param vanity_url: The "Custom URL" that can also be used to preview the item on Box. Custom + URLs can only be created or modified in the Box Web application., defaults to None + :type vanity_url: Optional[str], optional + :param vanity_name: The custom name of a shared link, as used in the `vanity_url` field., defaults to None + :type vanity_name: Optional[str], optional + :param access: The access level for this shared link. + + * `open` - provides access to this item to anyone with this link + * `company` - only provides access to this item to people the same company + * `collaborators` - only provides access to this item to people who are + collaborators on this item + + If this field is omitted when creating the shared link, the access level + will be set to the default access level specified by the enterprise admin., defaults to None + :type access: Optional[FolderSharedLinkAccessField], optional + :param unshared_at: The date and time when this link will be unshared. This field can only be + set by users with paid accounts., defaults to None + :type unshared_at: Optional[DateTime], optional + :param permissions: Defines if this link allows a user to preview, edit, and download an item. + These permissions refer to the shared link only and + do not supersede permissions applied to the item itself., defaults to None + :type permissions: Optional[FolderSharedLinkPermissionsField], optional + """ + super().__init__(**kwargs) + self.url = url + self.effective_access = effective_access + self.effective_permission = effective_permission + self.is_password_enabled = is_password_enabled + self.download_count = download_count + self.preview_count = preview_count + self.download_url = download_url + self.vanity_url = vanity_url + self.vanity_name = vanity_name + self.access = access + self.unshared_at = unshared_at + self.permissions = permissions + + +class FolderFolderUploadEmailAccessField(str, Enum): + OPEN = 'open' + COLLABORATORS = 'collaborators' + + +class FolderFolderUploadEmailField(BaseObject): + def __init__( + self, + *, + access: Optional[FolderFolderUploadEmailAccessField] = None, + email: Optional[str] = None, + **kwargs + ): + """ + :param access: When this parameter has been set, users can email files + to the email address that has been automatically + created for this folder. + + To create an email address, set this property either when + creating or updating the folder. + + When set to `collaborators`, only emails from registered email + addresses for collaborators will be accepted. This includes + any email aliases a user might have registered. + + When set to `open` it will accept emails from any email + address., defaults to None + :type access: Optional[FolderFolderUploadEmailAccessField], optional + :param email: The optional upload email address for this folder., defaults to None + :type email: Optional[str], optional + """ + super().__init__(**kwargs) + self.access = access + self.email = email + + +class FolderItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class Folder(FolderMini): + def __init__( + self, + id: str, + *, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + description: Optional[str] = None, + size: Optional[int] = None, + path_collection: Optional[FolderPathCollectionField] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[FolderSharedLinkField] = None, + folder_upload_email: Optional[FolderFolderUploadEmailField] = None, + parent: Optional[FolderMini] = None, + item_status: Optional[FolderItemStatusField] = None, + item_collection: Optional[Items] = None, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + etag: Optional[str] = None, + type: FolderBaseTypeField = FolderBaseTypeField.FOLDER, + **kwargs + ): + """ + :param id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting a folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folders/123` + the `folder_id` is `123`. + :type id: str + :param created_at: The date and time when the folder was created. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The date and time when the folder was last updated. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type modified_at: Optional[DateTime], optional + :param size: The folder size in bytes. + + Be careful parsing this integer as its + value can get very large., defaults to None + :type size: Optional[int], optional + :param trashed_at: The time at which this folder was put in the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: The time at which this folder is expected to be purged + from the trash., defaults to None + :type purged_at: Optional[DateTime], optional + :param content_created_at: The date and time at which this folder was originally + created., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this folder was last updated., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param folder_upload_email: The `folder_upload_email` parameter is not `null` if one of the following options is **true**: + + * The **Allow uploads to this folder via email** and the **Only allow email uploads from collaborators in this folder** are [enabled for a folder in the Admin Console](https://support.box.com/hc/en-us/articles/360043697534-Upload-to-Box-Through-Email), and the user has at least **Upload** permissions granted. + + * The **Allow uploads to this folder via email** setting is enabled for a folder in the Admin Console, and the **Only allow email uploads from collaborators in this folder** setting is deactivated (unchecked). + + If the conditions are not met, the parameter will have the following value: `folder_upload_email: null`., defaults to None + :type folder_upload_email: Optional[FolderFolderUploadEmailField], optional + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash + * `trashed` when the item has been moved to the trash but not deleted + * `deleted` when the item has been permanently deleted., defaults to None + :type item_status: Optional[FolderItemStatusField], optional + :param name: The name of the folder., defaults to None + :type name: Optional[str], optional + :param etag: The HTTP `etag` of this folder. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the folder if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `folder`., defaults to FolderBaseTypeField.FOLDER + :type type: FolderBaseTypeField, optional + """ + super().__init__( + id=id, sequence_id=sequence_id, name=name, etag=etag, type=type, **kwargs + ) + self.created_at = created_at + self.modified_at = modified_at + self.description = description + self.size = size + self.path_collection = path_collection + self.created_by = created_by + self.modified_by = modified_by + self.trashed_at = trashed_at + self.purged_at = purged_at + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + self.owned_by = owned_by + self.shared_link = shared_link + self.folder_upload_email = folder_upload_email + self.parent = parent + self.item_status = item_status + self.item_collection = item_collection diff --git a/box_sdk_gen/schemas/folder_base.py b/box_sdk_gen/schemas/folder_base.py new file mode 100644 index 000000000..251e11c9d --- /dev/null +++ b/box_sdk_gen/schemas/folder_base.py @@ -0,0 +1,44 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class FolderBaseTypeField(str, Enum): + FOLDER = 'folder' + + +class FolderBase(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + etag: Optional[str] = None, + type: FolderBaseTypeField = FolderBaseTypeField.FOLDER, + **kwargs + ): + """ + :param id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting a folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folders/123` + the `folder_id` is `123`. + :type id: str + :param etag: The HTTP `etag` of this folder. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the folder if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `folder`., defaults to FolderBaseTypeField.FOLDER + :type type: FolderBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.etag = etag + self.type = type diff --git a/box_sdk_gen/schemas/folder_full.py b/box_sdk_gen/schemas/folder_full.py new file mode 100644 index 000000000..dff8b9f69 --- /dev/null +++ b/box_sdk_gen/schemas/folder_full.py @@ -0,0 +1,314 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.folder_base import FolderBaseTypeField + +from box_sdk_gen.schemas.folder_base import FolderBase + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.folder import FolderPathCollectionField + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.folder import FolderSharedLinkField + +from box_sdk_gen.schemas.folder import FolderFolderUploadEmailField + +from box_sdk_gen.schemas.folder import FolderItemStatusField + +from box_sdk_gen.schemas.items import Items + +from box_sdk_gen.schemas.folder import Folder + +from box_sdk_gen.schemas.metadata_full import MetadataFull + +from box_sdk_gen.box.errors import BoxSDKError + + +class FolderFullSyncStateField(str, Enum): + SYNCED = 'synced' + NOT_SYNCED = 'not_synced' + PARTIALLY_SYNCED = 'partially_synced' + + +class FolderFullPermissionsField(BaseObject): + def __init__( + self, + can_delete: bool, + can_download: bool, + can_invite_collaborator: bool, + can_rename: bool, + can_set_share_access: bool, + can_share: bool, + can_upload: bool, + **kwargs + ): + """ + :param can_delete: Specifies if the current user can delete this item. + :type can_delete: bool + :param can_download: Specifies if the current user can download this item. + :type can_download: bool + :param can_invite_collaborator: Specifies if the current user can invite new + users to collaborate on this item, and if the user can + update the role of a user already collaborated on this + item. + :type can_invite_collaborator: bool + :param can_rename: Specifies if the user can rename this item. + :type can_rename: bool + :param can_set_share_access: Specifies if the user can change the access level of an + existing shared link on this item. + :type can_set_share_access: bool + :param can_share: Specifies if the user can create a shared link for this item. + :type can_share: bool + :param can_upload: Specifies if the user can upload into this folder. + :type can_upload: bool + """ + super().__init__(**kwargs) + self.can_delete = can_delete + self.can_download = can_download + self.can_invite_collaborator = can_invite_collaborator + self.can_rename = can_rename + self.can_set_share_access = can_set_share_access + self.can_share = can_share + self.can_upload = can_upload + + +class FolderFullMetadataField(BaseObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.extra_data = kwargs + + +class FolderFullAllowedSharedLinkAccessLevelsField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class FolderFullAllowedInviteeRolesField(str, Enum): + EDITOR = 'editor' + VIEWER = 'viewer' + PREVIEWER = 'previewer' + UPLOADER = 'uploader' + PREVIEWER_UPLOADER = 'previewer uploader' + VIEWER_UPLOADER = 'viewer uploader' + CO_OWNER = 'co-owner' + + +class FolderFullWatermarkInfoField(BaseObject): + def __init__(self, *, is_watermarked: Optional[bool] = None, **kwargs): + """ + :param is_watermarked: Specifies if this item has a watermark applied., defaults to None + :type is_watermarked: Optional[bool], optional + """ + super().__init__(**kwargs) + self.is_watermarked = is_watermarked + + +class FolderFullClassificationField(BaseObject): + def __init__( + self, + *, + name: Optional[str] = None, + definition: Optional[str] = None, + color: Optional[str] = None, + **kwargs + ): + """ + :param name: The name of the classification., defaults to None + :type name: Optional[str], optional + :param definition: An explanation of the meaning of this classification., defaults to None + :type definition: Optional[str], optional + :param color: The color that is used to display the + classification label in a user-interface. Colors are defined by the admin + or co-admin who created the classification in the Box web app., defaults to None + :type color: Optional[str], optional + """ + super().__init__(**kwargs) + self.name = name + self.definition = definition + self.color = color + + +class FolderFull(Folder): + def __init__( + self, + id: str, + *, + sync_state: Optional[FolderFullSyncStateField] = None, + has_collaborations: Optional[bool] = None, + permissions: Optional[FolderFullPermissionsField] = None, + tags: Optional[List[str]] = None, + can_non_owners_invite: Optional[bool] = None, + is_externally_owned: Optional[bool] = None, + metadata: Optional[FolderFullMetadataField] = None, + is_collaboration_restricted_to_enterprise: Optional[bool] = None, + allowed_shared_link_access_levels: Optional[ + List[FolderFullAllowedSharedLinkAccessLevelsField] + ] = None, + allowed_invitee_roles: Optional[ + List[FolderFullAllowedInviteeRolesField] + ] = None, + watermark_info: Optional[FolderFullWatermarkInfoField] = None, + is_accessible_via_shared_link: Optional[bool] = None, + can_non_owners_view_collaborators: Optional[bool] = None, + classification: Optional[FolderFullClassificationField] = None, + is_associated_with_app_item: Optional[bool] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + description: Optional[str] = None, + size: Optional[int] = None, + path_collection: Optional[FolderPathCollectionField] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[FolderSharedLinkField] = None, + folder_upload_email: Optional[FolderFolderUploadEmailField] = None, + parent: Optional[FolderMini] = None, + item_status: Optional[FolderItemStatusField] = None, + item_collection: Optional[Items] = None, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + etag: Optional[str] = None, + type: FolderBaseTypeField = FolderBaseTypeField.FOLDER, + **kwargs + ): + """ + :param id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting a folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folders/123` + the `folder_id` is `123`. + :type id: str + :param has_collaborations: Specifies if this folder has any other collaborators., defaults to None + :type has_collaborations: Optional[bool], optional + :param is_externally_owned: Specifies if this folder is owned by a user outside of the + authenticated enterprise., defaults to None + :type is_externally_owned: Optional[bool], optional + :param allowed_shared_link_access_levels: A list of access levels that are available + for this folder. + + For some folders, like the root folder, this will always + be an empty list as sharing is not allowed at that level., defaults to None + :type allowed_shared_link_access_levels: Optional[List[FolderFullAllowedSharedLinkAccessLevelsField]], optional + :param allowed_invitee_roles: A list of the types of roles that user can be invited at + when sharing this folder., defaults to None + :type allowed_invitee_roles: Optional[List[FolderFullAllowedInviteeRolesField]], optional + :param is_accessible_via_shared_link: Specifies if the folder can be accessed + with the direct shared link or a shared link + to a parent folder., defaults to None + :type is_accessible_via_shared_link: Optional[bool], optional + :param can_non_owners_view_collaborators: Specifies if collaborators who are not owners + of this folder are restricted from viewing other + collaborations on this folder. + + It also restricts non-owners from inviting new + collaborators., defaults to None + :type can_non_owners_view_collaborators: Optional[bool], optional + :param is_associated_with_app_item: This field will return true if the folder or any ancestor of the + folder is associated with at least one app item. Note that this will + return true even if the context user does not have access to the + app item(s) associated with the folder., defaults to None + :type is_associated_with_app_item: Optional[bool], optional + :param created_at: The date and time when the folder was created. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The date and time when the folder was last updated. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type modified_at: Optional[DateTime], optional + :param size: The folder size in bytes. + + Be careful parsing this integer as its + value can get very large., defaults to None + :type size: Optional[int], optional + :param trashed_at: The time at which this folder was put in the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: The time at which this folder is expected to be purged + from the trash., defaults to None + :type purged_at: Optional[DateTime], optional + :param content_created_at: The date and time at which this folder was originally + created., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this folder was last updated., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param folder_upload_email: The `folder_upload_email` parameter is not `null` if one of the following options is **true**: + + * The **Allow uploads to this folder via email** and the **Only allow email uploads from collaborators in this folder** are [enabled for a folder in the Admin Console](https://support.box.com/hc/en-us/articles/360043697534-Upload-to-Box-Through-Email), and the user has at least **Upload** permissions granted. + + * The **Allow uploads to this folder via email** setting is enabled for a folder in the Admin Console, and the **Only allow email uploads from collaborators in this folder** setting is deactivated (unchecked). + + If the conditions are not met, the parameter will have the following value: `folder_upload_email: null`., defaults to None + :type folder_upload_email: Optional[FolderFolderUploadEmailField], optional + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash + * `trashed` when the item has been moved to the trash but not deleted + * `deleted` when the item has been permanently deleted., defaults to None + :type item_status: Optional[FolderItemStatusField], optional + :param name: The name of the folder., defaults to None + :type name: Optional[str], optional + :param etag: The HTTP `etag` of this folder. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the folder if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `folder`., defaults to FolderBaseTypeField.FOLDER + :type type: FolderBaseTypeField, optional + """ + super().__init__( + id=id, + created_at=created_at, + modified_at=modified_at, + description=description, + size=size, + path_collection=path_collection, + created_by=created_by, + modified_by=modified_by, + trashed_at=trashed_at, + purged_at=purged_at, + content_created_at=content_created_at, + content_modified_at=content_modified_at, + owned_by=owned_by, + shared_link=shared_link, + folder_upload_email=folder_upload_email, + parent=parent, + item_status=item_status, + item_collection=item_collection, + sequence_id=sequence_id, + name=name, + etag=etag, + type=type, + **kwargs + ) + self.sync_state = sync_state + self.has_collaborations = has_collaborations + self.permissions = permissions + self.tags = tags + self.can_non_owners_invite = can_non_owners_invite + self.is_externally_owned = is_externally_owned + self.metadata = metadata + self.is_collaboration_restricted_to_enterprise = ( + is_collaboration_restricted_to_enterprise + ) + self.allowed_shared_link_access_levels = allowed_shared_link_access_levels + self.allowed_invitee_roles = allowed_invitee_roles + self.watermark_info = watermark_info + self.is_accessible_via_shared_link = is_accessible_via_shared_link + self.can_non_owners_view_collaborators = can_non_owners_view_collaborators + self.classification = classification + self.is_associated_with_app_item = is_associated_with_app_item diff --git a/box_sdk_gen/schemas/folder_lock.py b/box_sdk_gen/schemas/folder_lock.py new file mode 100644 index 000000000..ab97830ae --- /dev/null +++ b/box_sdk_gen/schemas/folder_lock.py @@ -0,0 +1,61 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class FolderLockLockedOperationsField(BaseObject): + def __init__(self, move: bool, delete: bool, **kwargs): + """ + :param move: Whether moving the folder is restricted. + :type move: bool + :param delete: Whether deleting the folder is restricted. + :type delete: bool + """ + super().__init__(**kwargs) + self.move = move + self.delete = delete + + +class FolderLock(BaseObject): + def __init__( + self, + *, + folder: Optional[FolderMini] = None, + id: Optional[str] = None, + type: Optional[str] = None, + created_by: Optional[UserBase] = None, + created_at: Optional[DateTime] = None, + locked_operations: Optional[FolderLockLockedOperationsField] = None, + lock_type: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this folder lock., defaults to None + :type id: Optional[str], optional + :param type: The object type, always `folder_lock`., defaults to None + :type type: Optional[str], optional + :param created_at: When the folder lock object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param locked_operations: The operations that have been locked. Currently the `move` + and `delete` operations cannot be locked separately, and both need to be + set to `true`., defaults to None + :type locked_operations: Optional[FolderLockLockedOperationsField], optional + :param lock_type: The lock type, always `freeze`., defaults to None + :type lock_type: Optional[str], optional + """ + super().__init__(**kwargs) + self.folder = folder + self.id = id + self.type = type + self.created_by = created_by + self.created_at = created_at + self.locked_operations = locked_operations + self.lock_type = lock_type diff --git a/box_sdk_gen/schemas/folder_locks.py b/box_sdk_gen/schemas/folder_locks.py new file mode 100644 index 000000000..6f0ca8c5f --- /dev/null +++ b/box_sdk_gen/schemas/folder_locks.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.folder_lock import FolderLock + +from box_sdk_gen.box.errors import BoxSDKError + + +class FolderLocks(BaseObject): + def __init__( + self, + *, + entries: Optional[List[FolderLock]] = None, + limit: Optional[str] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: A list of folder locks., defaults to None + :type entries: Optional[List[FolderLock]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[str], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/folder_mini.py b/box_sdk_gen/schemas/folder_mini.py new file mode 100644 index 000000000..1e7d71a14 --- /dev/null +++ b/box_sdk_gen/schemas/folder_mini.py @@ -0,0 +1,41 @@ +from typing import Optional + +from box_sdk_gen.schemas.folder_base import FolderBaseTypeField + +from box_sdk_gen.schemas.folder_base import FolderBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class FolderMini(FolderBase): + def __init__( + self, + id: str, + *, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + etag: Optional[str] = None, + type: FolderBaseTypeField = FolderBaseTypeField.FOLDER, + **kwargs + ): + """ + :param id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting a folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folders/123` + the `folder_id` is `123`. + :type id: str + :param name: The name of the folder., defaults to None + :type name: Optional[str], optional + :param etag: The HTTP `etag` of this folder. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the folder if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `folder`., defaults to FolderBaseTypeField.FOLDER + :type type: FolderBaseTypeField, optional + """ + super().__init__(id=id, etag=etag, type=type, **kwargs) + self.sequence_id = sequence_id + self.name = name diff --git a/box_sdk_gen/schemas/folder_reference.py b/box_sdk_gen/schemas/folder_reference.py new file mode 100644 index 000000000..a99575569 --- /dev/null +++ b/box_sdk_gen/schemas/folder_reference.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class FolderReferenceTypeField(str, Enum): + FOLDER = 'folder' + + +class FolderReference(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + type: FolderReferenceTypeField = FolderReferenceTypeField.FOLDER, + **kwargs + ): + """ + :param id: ID of the folder. + :type id: str + :param type: The value will always be `folder`., defaults to FolderReferenceTypeField.FOLDER + :type type: FolderReferenceTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/generic_source.py b/box_sdk_gen/schemas/generic_source.py new file mode 100644 index 000000000..e476ab914 --- /dev/null +++ b/box_sdk_gen/schemas/generic_source.py @@ -0,0 +1 @@ +from box_sdk_gen.box.errors import BoxSDKError diff --git a/box_sdk_gen/schemas/group.py b/box_sdk_gen/schemas/group.py new file mode 100644 index 000000000..fbb2fdc88 --- /dev/null +++ b/box_sdk_gen/schemas/group.py @@ -0,0 +1,44 @@ +from typing import Optional + +from box_sdk_gen.schemas.group_base import GroupBaseTypeField + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.schemas.group_mini import GroupMiniGroupTypeField + +from box_sdk_gen.schemas.group_mini import GroupMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class Group(GroupMini): + def __init__( + self, + id: str, + *, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + name: Optional[str] = None, + group_type: Optional[GroupMiniGroupTypeField] = None, + type: GroupBaseTypeField = GroupBaseTypeField.GROUP, + **kwargs + ): + """ + :param id: The unique identifier for this object. + :type id: str + :param created_at: When the group object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the group object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param name: The name of the group., defaults to None + :type name: Optional[str], optional + :param group_type: The type of the group., defaults to None + :type group_type: Optional[GroupMiniGroupTypeField], optional + :param type: The value will always be `group`., defaults to GroupBaseTypeField.GROUP + :type type: GroupBaseTypeField, optional + """ + super().__init__(id=id, name=name, group_type=group_type, type=type, **kwargs) + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/group_base.py b/box_sdk_gen/schemas/group_base.py new file mode 100644 index 000000000..38cf63cd1 --- /dev/null +++ b/box_sdk_gen/schemas/group_base.py @@ -0,0 +1,26 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class GroupBaseTypeField(str, Enum): + GROUP = 'group' + + +class GroupBase(BaseObject): + _discriminator = 'type', {'group'} + + def __init__( + self, id: str, *, type: GroupBaseTypeField = GroupBaseTypeField.GROUP, **kwargs + ): + """ + :param id: The unique identifier for this object. + :type id: str + :param type: The value will always be `group`., defaults to GroupBaseTypeField.GROUP + :type type: GroupBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/group_full.py b/box_sdk_gen/schemas/group_full.py new file mode 100644 index 000000000..c0503abfa --- /dev/null +++ b/box_sdk_gen/schemas/group_full.py @@ -0,0 +1,128 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.group_base import GroupBaseTypeField + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.schemas.group_mini import GroupMiniGroupTypeField + +from box_sdk_gen.schemas.group_mini import GroupMini + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.group import Group + +from box_sdk_gen.box.errors import BoxSDKError + + +class GroupFullInvitabilityLevelField(str, Enum): + ADMINS_ONLY = 'admins_only' + ADMINS_AND_MEMBERS = 'admins_and_members' + ALL_MANAGED_USERS = 'all_managed_users' + + +class GroupFullMemberViewabilityLevelField(str, Enum): + ADMINS_ONLY = 'admins_only' + ADMINS_AND_MEMBERS = 'admins_and_members' + ALL_MANAGED_USERS = 'all_managed_users' + + +class GroupFullPermissionsField(BaseObject): + def __init__(self, *, can_invite_as_collaborator: Optional[bool] = None, **kwargs): + """ + :param can_invite_as_collaborator: Specifies if the user can invite the group to collaborate on any items., defaults to None + :type can_invite_as_collaborator: Optional[bool], optional + """ + super().__init__(**kwargs) + self.can_invite_as_collaborator = can_invite_as_collaborator + + +class GroupFull(Group): + def __init__( + self, + id: str, + *, + provenance: Optional[str] = None, + external_sync_identifier: Optional[str] = None, + description: Optional[str] = None, + invitability_level: Optional[GroupFullInvitabilityLevelField] = None, + member_viewability_level: Optional[GroupFullMemberViewabilityLevelField] = None, + permissions: Optional[GroupFullPermissionsField] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + name: Optional[str] = None, + group_type: Optional[GroupMiniGroupTypeField] = None, + type: GroupBaseTypeField = GroupBaseTypeField.GROUP, + **kwargs + ): + """ + :param id: The unique identifier for this object. + :type id: str + :param provenance: Keeps track of which external source this group is + coming from (e.g. "Active Directory", "Google Groups", + "Facebook Groups"). Setting this will + also prevent Box users from editing the group name + and its members directly via the Box web application. + This is desirable for one-way syncing of groups., defaults to None + :type provenance: Optional[str], optional + :param external_sync_identifier: An arbitrary identifier that can be used by + external group sync tools to link this Box Group to + an external group. Example values of this field + could be an Active Directory Object ID or a Google + Group ID. We recommend you use of this field in + order to avoid issues when group names are updated in + either Box or external systems., defaults to None + :type external_sync_identifier: Optional[str], optional + :param description: Human readable description of the group., defaults to None + :type description: Optional[str], optional + :param invitability_level: Specifies who can invite the group to collaborate + on items. + + When set to `admins_only` the enterprise admin, co-admins, + and the group's admin can invite the group. + + When set to `admins_and_members` all the admins listed + above and group members can invite the group. + + When set to `all_managed_users` all managed users in the + enterprise can invite the group., defaults to None + :type invitability_level: Optional[GroupFullInvitabilityLevelField], optional + :param member_viewability_level: Specifies who can view the members of the group + (Get Memberships for Group). + + * `admins_only` - the enterprise admin, co-admins, group's + group admin. + * `admins_and_members` - all admins and group members. + * `all_managed_users` - all managed users in the + enterprise., defaults to None + :type member_viewability_level: Optional[GroupFullMemberViewabilityLevelField], optional + :param created_at: When the group object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the group object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param name: The name of the group., defaults to None + :type name: Optional[str], optional + :param group_type: The type of the group., defaults to None + :type group_type: Optional[GroupMiniGroupTypeField], optional + :param type: The value will always be `group`., defaults to GroupBaseTypeField.GROUP + :type type: GroupBaseTypeField, optional + """ + super().__init__( + id=id, + created_at=created_at, + modified_at=modified_at, + name=name, + group_type=group_type, + type=type, + **kwargs + ) + self.provenance = provenance + self.external_sync_identifier = external_sync_identifier + self.description = description + self.invitability_level = invitability_level + self.member_viewability_level = member_viewability_level + self.permissions = permissions diff --git a/box_sdk_gen/schemas/group_membership.py b/box_sdk_gen/schemas/group_membership.py new file mode 100644 index 000000000..48cc01ea6 --- /dev/null +++ b/box_sdk_gen/schemas/group_membership.py @@ -0,0 +1,59 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.group_mini import GroupMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class GroupMembershipTypeField(str, Enum): + GROUP_MEMBERSHIP = 'group_membership' + + +class GroupMembershipRoleField(str, Enum): + MEMBER = 'member' + ADMIN = 'admin' + + +class GroupMembership(BaseObject): + _discriminator = 'type', {'group_membership'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[GroupMembershipTypeField] = None, + user: Optional[UserMini] = None, + group: Optional[GroupMini] = None, + role: Optional[GroupMembershipRoleField] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param id: The unique identifier for this group membership., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `group_membership`., defaults to None + :type type: Optional[GroupMembershipTypeField], optional + :param role: The role of the user in the group., defaults to None + :type role: Optional[GroupMembershipRoleField], optional + :param created_at: The time this membership was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The time this membership was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.user = user + self.group = group + self.role = role + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/group_memberships.py b/box_sdk_gen/schemas/group_memberships.py new file mode 100644 index 000000000..a5b402504 --- /dev/null +++ b/box_sdk_gen/schemas/group_memberships.py @@ -0,0 +1,80 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.group_membership import GroupMembership + +from box_sdk_gen.box.errors import BoxSDKError + + +class GroupMembershipsOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class GroupMembershipsOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[GroupMembershipsOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[GroupMembershipsOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class GroupMemberships(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[GroupMembershipsOrderField]] = None, + entries: Optional[List[GroupMembership]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[GroupMembershipsOrderField]], optional + :param entries: A list of group memberships., defaults to None + :type entries: Optional[List[GroupMembership]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/group_mini.py b/box_sdk_gen/schemas/group_mini.py new file mode 100644 index 000000000..a05571868 --- /dev/null +++ b/box_sdk_gen/schemas/group_mini.py @@ -0,0 +1,39 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.group_base import GroupBaseTypeField + +from box_sdk_gen.schemas.group_base import GroupBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class GroupMiniGroupTypeField(str, Enum): + MANAGED_GROUP = 'managed_group' + ALL_USERS_GROUP = 'all_users_group' + + +class GroupMini(GroupBase): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + group_type: Optional[GroupMiniGroupTypeField] = None, + type: GroupBaseTypeField = GroupBaseTypeField.GROUP, + **kwargs + ): + """ + :param id: The unique identifier for this object. + :type id: str + :param name: The name of the group., defaults to None + :type name: Optional[str], optional + :param group_type: The type of the group., defaults to None + :type group_type: Optional[GroupMiniGroupTypeField], optional + :param type: The value will always be `group`., defaults to GroupBaseTypeField.GROUP + :type type: GroupBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name + self.group_type = group_type diff --git a/box_sdk_gen/schemas/groups.py b/box_sdk_gen/schemas/groups.py new file mode 100644 index 000000000..76f8a6c13 --- /dev/null +++ b/box_sdk_gen/schemas/groups.py @@ -0,0 +1,80 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.group_full import GroupFull + +from box_sdk_gen.box.errors import BoxSDKError + + +class GroupsOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class GroupsOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[GroupsOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[GroupsOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class Groups(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[GroupsOrderField]] = None, + entries: Optional[List[GroupFull]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[GroupsOrderField]], optional + :param entries: A list of groups., defaults to None + :type entries: Optional[List[GroupFull]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/integration_mapping.py b/box_sdk_gen/schemas/integration_mapping.py new file mode 100644 index 000000000..be4efb56a --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping.py @@ -0,0 +1,88 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.integration_mapping_base import IntegrationMappingBaseTypeField + +from box_sdk_gen.schemas.integration_mapping_base import IntegrationMappingBase + +from box_sdk_gen.schemas.integration_mapping_slack_options import ( + IntegrationMappingSlackOptions, +) + +from box_sdk_gen.schemas.user_integration_mappings import UserIntegrationMappings + +from box_sdk_gen.schemas.integration_mapping_partner_item_slack import ( + IntegrationMappingPartnerItemSlack, +) + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class IntegrationMappingIntegrationTypeField(str, Enum): + SLACK = 'slack' + + +class IntegrationMapping(IntegrationMappingBase): + def __init__( + self, + partner_item: IntegrationMappingPartnerItemSlack, + box_item: FolderMini, + id: str, + *, + integration_type: Optional[IntegrationMappingIntegrationTypeField] = None, + is_manually_created: Optional[bool] = None, + options: Optional[IntegrationMappingSlackOptions] = None, + created_by: Optional[UserIntegrationMappings] = None, + modified_by: Optional[UserIntegrationMappings] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + type: IntegrationMappingBaseTypeField = IntegrationMappingBaseTypeField.INTEGRATION_MAPPING, + **kwargs + ): + """ + :param partner_item: Mapped item object for Slack. + :type partner_item: IntegrationMappingPartnerItemSlack + :param box_item: The Box folder, to which the object from the + partner app domain (referenced in `partner_item_id`) is mapped. + :type box_item: FolderMini + :param id: A unique identifier of a folder mapping + (part of a composite key together + with `integration_type`). + :type id: str + :param integration_type: Identifies the Box partner app, + with which the mapping is associated. + Currently only supports Slack. + (part of the composite key together with `id`)., defaults to None + :type integration_type: Optional[IntegrationMappingIntegrationTypeField], optional + :param is_manually_created: Identifies whether the mapping has + been manually set + (as opposed to being automatically created)., defaults to None + :type is_manually_created: Optional[bool], optional + :param created_by: An object representing the user who + created the integration mapping., defaults to None + :type created_by: Optional[UserIntegrationMappings], optional + :param modified_by: The user who + last modified the integration mapping., defaults to None + :type modified_by: Optional[UserIntegrationMappings], optional + :param created_at: When the integration mapping object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the integration mapping object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param type: Mapping type., defaults to IntegrationMappingBaseTypeField.INTEGRATION_MAPPING + :type type: IntegrationMappingBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.partner_item = partner_item + self.box_item = box_item + self.integration_type = integration_type + self.is_manually_created = is_manually_created + self.options = options + self.created_by = created_by + self.modified_by = modified_by + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/integration_mapping_base.py b/box_sdk_gen/schemas/integration_mapping_base.py new file mode 100644 index 000000000..eb53aadb0 --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_base.py @@ -0,0 +1,32 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingBaseTypeField(str, Enum): + INTEGRATION_MAPPING = 'integration_mapping' + + +class IntegrationMappingBase(BaseObject): + _discriminator = 'type', {'integration_mapping'} + + def __init__( + self, + id: str, + *, + type: IntegrationMappingBaseTypeField = IntegrationMappingBaseTypeField.INTEGRATION_MAPPING, + **kwargs + ): + """ + :param id: A unique identifier of a folder mapping + (part of a composite key together + with `integration_type`). + :type id: str + :param type: Mapping type., defaults to IntegrationMappingBaseTypeField.INTEGRATION_MAPPING + :type type: IntegrationMappingBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/integration_mapping_box_item_slack.py b/box_sdk_gen/schemas/integration_mapping_box_item_slack.py new file mode 100644 index 000000000..427835d3a --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_box_item_slack.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingBoxItemSlackTypeField(str, Enum): + FOLDER = 'folder' + + +class IntegrationMappingBoxItemSlack(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + type: IntegrationMappingBoxItemSlackTypeField = IntegrationMappingBoxItemSlackTypeField.FOLDER, + **kwargs + ): + """ + :param id: ID of the mapped item (of type referenced in `type`). + :type id: str + :param type: Type of the mapped item referenced in `id`., defaults to IntegrationMappingBoxItemSlackTypeField.FOLDER + :type type: IntegrationMappingBoxItemSlackTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/integration_mapping_partner_item_slack.py b/box_sdk_gen/schemas/integration_mapping_partner_item_slack.py new file mode 100644 index 000000000..5d0731166 --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_partner_item_slack.py @@ -0,0 +1,40 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingPartnerItemSlackTypeField(str, Enum): + CHANNEL = 'channel' + + +class IntegrationMappingPartnerItemSlack(BaseObject): + _discriminator = 'type', {'channel'} + + def __init__( + self, + id: str, + *, + type: IntegrationMappingPartnerItemSlackTypeField = IntegrationMappingPartnerItemSlackTypeField.CHANNEL, + slack_workspace_id: Optional[str] = None, + slack_org_id: Optional[str] = None, + **kwargs + ): + """ + :param id: ID of the mapped item (of type referenced in `type`). + :type id: str + :param type: Type of the mapped item referenced in `id`., defaults to IntegrationMappingPartnerItemSlackTypeField.CHANNEL + :type type: IntegrationMappingPartnerItemSlackTypeField, optional + :param slack_workspace_id: ID of the Slack workspace with which the item is associated. Use this parameter if Box for Slack is installed at a workspace level. Do not use `slack_org_id` at the same time., defaults to None + :type slack_workspace_id: Optional[str], optional + :param slack_org_id: ID of the Slack org with which the item is associated. Use this parameter if Box for Slack is installed at the org level. Do not use `slack_workspace_id` at the same time., defaults to None + :type slack_org_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.slack_workspace_id = slack_workspace_id + self.slack_org_id = slack_org_id diff --git a/box_sdk_gen/schemas/integration_mapping_partner_item_teams.py b/box_sdk_gen/schemas/integration_mapping_partner_item_teams.py new file mode 100644 index 000000000..4d5c0918a --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_partner_item_teams.py @@ -0,0 +1,34 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingPartnerItemTeamsTypeField(str, Enum): + CHANNEL = 'channel' + TEAM = 'team' + + +class IntegrationMappingPartnerItemTeams(BaseObject): + _discriminator = 'type', {'channel', 'team'} + + def __init__( + self, + type: IntegrationMappingPartnerItemTeamsTypeField, + id: str, + tenant_id: str, + **kwargs + ): + """ + :param type: Type of the mapped item referenced in `id`. + :type type: IntegrationMappingPartnerItemTeamsTypeField + :param id: ID of the mapped item (of type referenced in `type`). + :type id: str + :param tenant_id: ID of the tenant that is registered with Microsoft Teams. + :type tenant_id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.tenant_id = tenant_id diff --git a/box_sdk_gen/schemas/integration_mapping_partner_item_teams_create_request.py b/box_sdk_gen/schemas/integration_mapping_partner_item_teams_create_request.py new file mode 100644 index 000000000..f6f10fc43 --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_partner_item_teams_create_request.py @@ -0,0 +1,38 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingPartnerItemTeamsCreateRequestTypeField(str, Enum): + CHANNEL = 'channel' + TEAM = 'team' + + +class IntegrationMappingPartnerItemTeamsCreateRequest(BaseObject): + _discriminator = 'type', {'channel', 'team'} + + def __init__( + self, + type: IntegrationMappingPartnerItemTeamsCreateRequestTypeField, + id: str, + tenant_id: str, + team_id: str, + **kwargs + ): + """ + :param type: Type of the mapped item referenced in `id`. + :type type: IntegrationMappingPartnerItemTeamsCreateRequestTypeField + :param id: ID of the mapped item (of type referenced in `type`). + :type id: str + :param tenant_id: ID of the tenant that is registered with Microsoft Teams. + :type tenant_id: str + :param team_id: ID of the team that is registered with Microsoft Teams. + :type team_id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.tenant_id = tenant_id + self.team_id = team_id diff --git a/box_sdk_gen/schemas/integration_mapping_slack_create_request.py b/box_sdk_gen/schemas/integration_mapping_slack_create_request.py new file mode 100644 index 000000000..85586fe6b --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_slack_create_request.py @@ -0,0 +1,32 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.integration_mapping_partner_item_slack import ( + IntegrationMappingPartnerItemSlack, +) + +from box_sdk_gen.schemas.integration_mapping_box_item_slack import ( + IntegrationMappingBoxItemSlack, +) + +from box_sdk_gen.schemas.integration_mapping_slack_options import ( + IntegrationMappingSlackOptions, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingSlackCreateRequest(BaseObject): + def __init__( + self, + partner_item: IntegrationMappingPartnerItemSlack, + box_item: IntegrationMappingBoxItemSlack, + *, + options: Optional[IntegrationMappingSlackOptions] = None, + **kwargs + ): + super().__init__(**kwargs) + self.partner_item = partner_item + self.box_item = box_item + self.options = options diff --git a/box_sdk_gen/schemas/integration_mapping_slack_options.py b/box_sdk_gen/schemas/integration_mapping_slack_options.py new file mode 100644 index 000000000..701d27a82 --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_slack_options.py @@ -0,0 +1,21 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingSlackOptions(BaseObject): + def __init__( + self, *, is_access_management_disabled: Optional[bool] = None, **kwargs + ): + """ + :param is_access_management_disabled: Indicates whether or not channel member + access to the underlying box item + should be automatically managed. + Depending on type of channel, access is managed + through creating collaborations or shared links., defaults to None + :type is_access_management_disabled: Optional[bool], optional + """ + super().__init__(**kwargs) + self.is_access_management_disabled = is_access_management_disabled diff --git a/box_sdk_gen/schemas/integration_mapping_teams.py b/box_sdk_gen/schemas/integration_mapping_teams.py new file mode 100644 index 000000000..d4df1fafc --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_teams.py @@ -0,0 +1,67 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.integration_mapping_base import IntegrationMappingBaseTypeField + +from box_sdk_gen.schemas.integration_mapping_base import IntegrationMappingBase + +from box_sdk_gen.schemas.integration_mapping_partner_item_teams import ( + IntegrationMappingPartnerItemTeams, +) + +from box_sdk_gen.schemas.folder_reference import FolderReference + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class IntegrationMappingTeamsIntegrationTypeField(str, Enum): + TEAMS = 'teams' + + +class IntegrationMappingTeams(IntegrationMappingBase): + def __init__( + self, + partner_item: IntegrationMappingPartnerItemTeams, + box_item: FolderReference, + id: str, + *, + integration_type: Optional[IntegrationMappingTeamsIntegrationTypeField] = None, + is_overridden_by_manual_mapping: Optional[bool] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + type: IntegrationMappingBaseTypeField = IntegrationMappingBaseTypeField.INTEGRATION_MAPPING, + **kwargs + ): + """ + :param partner_item: Mapped item object for Teams. + :type partner_item: IntegrationMappingPartnerItemTeams + :param id: A unique identifier of a folder mapping + (part of a composite key together + with `integration_type`). + :type id: str + :param integration_type: Identifies the Box partner app, + with which the mapping is associated. + Supports Slack and Teams. + (part of the composite key together with `id`)., defaults to None + :type integration_type: Optional[IntegrationMappingTeamsIntegrationTypeField], optional + :param is_overridden_by_manual_mapping: Identifies whether the mapping has + been manually set by the team owner from UI for channels + (as opposed to being automatically created)., defaults to None + :type is_overridden_by_manual_mapping: Optional[bool], optional + :param created_at: When the integration mapping object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the integration mapping object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param type: Mapping type., defaults to IntegrationMappingBaseTypeField.INTEGRATION_MAPPING + :type type: IntegrationMappingBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.partner_item = partner_item + self.box_item = box_item + self.integration_type = integration_type + self.is_overridden_by_manual_mapping = is_overridden_by_manual_mapping + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/integration_mapping_teams_create_request.py b/box_sdk_gen/schemas/integration_mapping_teams_create_request.py new file mode 100644 index 000000000..2f615a43d --- /dev/null +++ b/box_sdk_gen/schemas/integration_mapping_teams_create_request.py @@ -0,0 +1,21 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.integration_mapping_partner_item_teams_create_request import ( + IntegrationMappingPartnerItemTeamsCreateRequest, +) + +from box_sdk_gen.schemas.folder_reference import FolderReference + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingTeamsCreateRequest(BaseObject): + def __init__( + self, + partner_item: IntegrationMappingPartnerItemTeamsCreateRequest, + box_item: FolderReference, + **kwargs + ): + super().__init__(**kwargs) + self.partner_item = partner_item + self.box_item = box_item diff --git a/box_sdk_gen/schemas/integration_mappings.py b/box_sdk_gen/schemas/integration_mappings.py new file mode 100644 index 000000000..638165d7e --- /dev/null +++ b/box_sdk_gen/schemas/integration_mappings.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.integration_mapping import IntegrationMapping + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappings(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + entries: Optional[List[IntegrationMapping]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param entries: A list of integration mappings., defaults to None + :type entries: Optional[List[IntegrationMapping]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/integration_mappings_teams.py b/box_sdk_gen/schemas/integration_mappings_teams.py new file mode 100644 index 000000000..f727ff6c2 --- /dev/null +++ b/box_sdk_gen/schemas/integration_mappings_teams.py @@ -0,0 +1,21 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.integration_mapping_teams import IntegrationMappingTeams + +from box_sdk_gen.box.errors import BoxSDKError + + +class IntegrationMappingsTeams(BaseObject): + def __init__( + self, *, entries: Optional[List[IntegrationMappingTeams]] = None, **kwargs + ): + """ + :param entries: A list of integration mappings., defaults to None + :type entries: Optional[List[IntegrationMappingTeams]], optional + """ + super().__init__(**kwargs) + self.entries = entries diff --git a/box_sdk_gen/schemas/invite.py b/box_sdk_gen/schemas/invite.py new file mode 100644 index 000000000..28487d988 --- /dev/null +++ b/box_sdk_gen/schemas/invite.py @@ -0,0 +1,85 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class InviteTypeField(str, Enum): + INVITE = 'invite' + + +class InviteInvitedToTypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class InviteInvitedToField(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[InviteInvitedToTypeField] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this enterprise., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[InviteInvitedToTypeField], optional + :param name: The name of the enterprise., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + + +class Invite(BaseObject): + _discriminator = 'type', {'invite'} + + def __init__( + self, + id: str, + *, + type: InviteTypeField = InviteTypeField.INVITE, + invited_to: Optional[InviteInvitedToField] = None, + actionable_by: Optional[UserMini] = None, + invited_by: Optional[UserMini] = None, + status: Optional[str] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param id: The unique identifier for this invite. + :type id: str + :param type: The value will always be `invite`., defaults to InviteTypeField.INVITE + :type type: InviteTypeField, optional + :param invited_to: A representation of a Box enterprise., defaults to None + :type invited_to: Optional[InviteInvitedToField], optional + :param status: The status of the invite., defaults to None + :type status: Optional[str], optional + :param created_at: When the invite was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the invite was modified., defaults to None + :type modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.invited_to = invited_to + self.actionable_by = actionable_by + self.invited_by = invited_by + self.status = status + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/item.py b/box_sdk_gen/schemas/item.py new file mode 100644 index 000000000..cb203e143 --- /dev/null +++ b/box_sdk_gen/schemas/item.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.box.errors import BoxSDKError + +Item = Union[FileFull, FolderMini, WebLink] diff --git a/box_sdk_gen/schemas/items.py b/box_sdk_gen/schemas/items.py new file mode 100644 index 000000000..f87c48e5b --- /dev/null +++ b/box_sdk_gen/schemas/items.py @@ -0,0 +1,94 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.item import Item + +from box_sdk_gen.box.errors import BoxSDKError + + +class ItemsOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class ItemsOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[ItemsOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[ItemsOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class Items(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + total_count: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[ItemsOrderField]] = None, + entries: Optional[List[Item]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[ItemsOrderField]], optional + :param entries: The items in this collection., defaults to None + :type entries: Optional[List[Item]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.total_count = total_count + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/items_offset_paginated.py b/box_sdk_gen/schemas/items_offset_paginated.py new file mode 100644 index 000000000..5549485b9 --- /dev/null +++ b/box_sdk_gen/schemas/items_offset_paginated.py @@ -0,0 +1,86 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.item import Item + +from box_sdk_gen.box.errors import BoxSDKError + + +class ItemsOffsetPaginatedOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class ItemsOffsetPaginatedOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[ItemsOffsetPaginatedOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[ItemsOffsetPaginatedOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class ItemsOffsetPaginated(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[ItemsOffsetPaginatedOrderField]] = None, + entries: Optional[List[Item]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[ItemsOffsetPaginatedOrderField]], optional + :param entries: The items in this collection., defaults to None + :type entries: Optional[List[Item]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/keyword_skill_card.py b/box_sdk_gen/schemas/keyword_skill_card.py new file mode 100644 index 000000000..ba6cf2cc7 --- /dev/null +++ b/box_sdk_gen/schemas/keyword_skill_card.py @@ -0,0 +1,139 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class KeywordSkillCardTypeField(str, Enum): + SKILL_CARD = 'skill_card' + + +class KeywordSkillCardSkillCardTypeField(str, Enum): + KEYWORD = 'keyword' + + +class KeywordSkillCardSkillCardTitleField(BaseObject): + def __init__(self, message: str, *, code: Optional[str] = None, **kwargs): + """ + :param message: The actual title to show in the UI. + :type message: str + :param code: An optional identifier for the title., defaults to None + :type code: Optional[str], optional + """ + super().__init__(**kwargs) + self.message = message + self.code = code + + +class KeywordSkillCardSkillTypeField(str, Enum): + SERVICE = 'service' + + +class KeywordSkillCardSkillField(BaseObject): + _discriminator = 'type', {'service'} + + def __init__( + self, + id: str, + *, + type: KeywordSkillCardSkillTypeField = KeywordSkillCardSkillTypeField.SERVICE, + **kwargs + ): + """ + :param id: A custom identifier that represent the service that + applied this metadata. + :type id: str + :param type: The value will always be `service`., defaults to KeywordSkillCardSkillTypeField.SERVICE + :type type: KeywordSkillCardSkillTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class KeywordSkillCardInvocationTypeField(str, Enum): + SKILL_INVOCATION = 'skill_invocation' + + +class KeywordSkillCardInvocationField(BaseObject): + _discriminator = 'type', {'skill_invocation'} + + def __init__( + self, + id: str, + *, + type: KeywordSkillCardInvocationTypeField = KeywordSkillCardInvocationTypeField.SKILL_INVOCATION, + **kwargs + ): + """ + :param id: A custom identifier that represent the instance of + the service that applied this metadata. For example, + if your `image-recognition-service` runs on multiple + nodes, this field can be used to identify the ID of + the node that was used to apply the metadata. + :type id: str + :param type: The value will always be `skill_invocation`., defaults to KeywordSkillCardInvocationTypeField.SKILL_INVOCATION + :type type: KeywordSkillCardInvocationTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class KeywordSkillCardEntriesField(BaseObject): + def __init__(self, *, text: Optional[str] = None, **kwargs): + """ + :param text: The text of the keyword., defaults to None + :type text: Optional[str], optional + """ + super().__init__(**kwargs) + self.text = text + + +class KeywordSkillCard(BaseObject): + _discriminator = 'skill_card_type', {'keyword'} + + def __init__( + self, + skill: KeywordSkillCardSkillField, + invocation: KeywordSkillCardInvocationField, + entries: List[KeywordSkillCardEntriesField], + *, + created_at: Optional[DateTime] = None, + type: KeywordSkillCardTypeField = KeywordSkillCardTypeField.SKILL_CARD, + skill_card_type: KeywordSkillCardSkillCardTypeField = KeywordSkillCardSkillCardTypeField.KEYWORD, + skill_card_title: Optional[KeywordSkillCardSkillCardTitleField] = None, + **kwargs + ): + """ + :param skill: The service that applied this metadata. + :type skill: KeywordSkillCardSkillField + :param invocation: The invocation of this service, used to track + which instance of a service applied the metadata. + :type invocation: KeywordSkillCardInvocationField + :param entries: An list of entries in the metadata card. + :type entries: List[KeywordSkillCardEntriesField] + :param created_at: The optional date and time this card was created at., defaults to None + :type created_at: Optional[DateTime], optional + :param type: The value will always be `skill_card`., defaults to KeywordSkillCardTypeField.SKILL_CARD + :type type: KeywordSkillCardTypeField, optional + :param skill_card_type: The value will always be `keyword`., defaults to KeywordSkillCardSkillCardTypeField.KEYWORD + :type skill_card_type: KeywordSkillCardSkillCardTypeField, optional + :param skill_card_title: The title of the card., defaults to None + :type skill_card_title: Optional[KeywordSkillCardSkillCardTitleField], optional + """ + super().__init__(**kwargs) + self.skill = skill + self.invocation = invocation + self.entries = entries + self.created_at = created_at + self.type = type + self.skill_card_type = skill_card_type + self.skill_card_title = skill_card_title diff --git a/box_sdk_gen/schemas/legal_hold_policies.py b/box_sdk_gen/schemas/legal_hold_policies.py new file mode 100644 index 000000000..52c42f9d1 --- /dev/null +++ b/box_sdk_gen/schemas/legal_hold_policies.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.legal_hold_policy import LegalHoldPolicy + +from box_sdk_gen.box.errors import BoxSDKError + + +class LegalHoldPolicies(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[LegalHoldPolicy]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of legal hold policies., defaults to None + :type entries: Optional[List[LegalHoldPolicy]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/legal_hold_policy.py b/box_sdk_gen/schemas/legal_hold_policy.py new file mode 100644 index 000000000..5a91fa884 --- /dev/null +++ b/box_sdk_gen/schemas/legal_hold_policy.py @@ -0,0 +1,122 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.legal_hold_policy_mini import LegalHoldPolicyMiniTypeField + +from box_sdk_gen.schemas.legal_hold_policy_mini import LegalHoldPolicyMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class LegalHoldPolicyStatusField(str, Enum): + ACTIVE = 'active' + APPLYING = 'applying' + RELEASING = 'releasing' + RELEASED = 'released' + + +class LegalHoldPolicyAssignmentCountsField(BaseObject): + def __init__( + self, + *, + user: Optional[int] = None, + folder: Optional[int] = None, + file: Optional[int] = None, + file_version: Optional[int] = None, + **kwargs + ): + """ + :param user: The number of users this policy is applied to., defaults to None + :type user: Optional[int], optional + :param folder: The number of folders this policy is applied to., defaults to None + :type folder: Optional[int], optional + :param file: The number of files this policy is applied to., defaults to None + :type file: Optional[int], optional + :param file_version: The number of file versions this policy is applied to., defaults to None + :type file_version: Optional[int], optional + """ + super().__init__(**kwargs) + self.user = user + self.folder = folder + self.file = file + self.file_version = file_version + + +class LegalHoldPolicy(LegalHoldPolicyMini): + def __init__( + self, + id: str, + *, + policy_name: Optional[str] = None, + description: Optional[str] = None, + status: Optional[LegalHoldPolicyStatusField] = None, + assignment_counts: Optional[LegalHoldPolicyAssignmentCountsField] = None, + created_by: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + deleted_at: Optional[DateTime] = None, + filter_started_at: Optional[DateTime] = None, + filter_ended_at: Optional[DateTime] = None, + release_notes: Optional[str] = None, + type: LegalHoldPolicyMiniTypeField = LegalHoldPolicyMiniTypeField.LEGAL_HOLD_POLICY, + **kwargs + ): + """ + :param id: The unique identifier for this legal hold policy. + :type id: str + :param policy_name: Name of the legal hold policy., defaults to None + :type policy_name: Optional[str], optional + :param description: Description of the legal hold policy. Optional + property with a 500 character limit., defaults to None + :type description: Optional[str], optional + :param status: Possible values: + * 'active' - the policy is not in a transition state. + * 'applying' - that the policy is in the process of + being applied. + * 'releasing' - that the process is in the process + of being released. + * 'released' - the policy is no longer active., defaults to None + :type status: Optional[LegalHoldPolicyStatusField], optional + :param assignment_counts: Counts of assignments within this a legal hold policy by item type., defaults to None + :type assignment_counts: Optional[LegalHoldPolicyAssignmentCountsField], optional + :param created_at: When the legal hold policy object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the legal hold policy object was modified. + Does not update when assignments are added or removed., defaults to None + :type modified_at: Optional[DateTime], optional + :param deleted_at: When the policy release request was sent. (Because + it can take time for a policy to fully delete, this + isn't quite the same time that the policy is fully deleted). + + If `null`, the policy was not deleted., defaults to None + :type deleted_at: Optional[DateTime], optional + :param filter_started_at: User-specified, optional date filter applies to + Custodian assignments only., defaults to None + :type filter_started_at: Optional[DateTime], optional + :param filter_ended_at: User-specified, optional date filter applies to + Custodian assignments only., defaults to None + :type filter_ended_at: Optional[DateTime], optional + :param release_notes: Optional notes about why the policy was created., defaults to None + :type release_notes: Optional[str], optional + :param type: The value will always be `legal_hold_policy`., defaults to LegalHoldPolicyMiniTypeField.LEGAL_HOLD_POLICY + :type type: LegalHoldPolicyMiniTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.policy_name = policy_name + self.description = description + self.status = status + self.assignment_counts = assignment_counts + self.created_by = created_by + self.created_at = created_at + self.modified_at = modified_at + self.deleted_at = deleted_at + self.filter_started_at = filter_started_at + self.filter_ended_at = filter_ended_at + self.release_notes = release_notes diff --git a/box_sdk_gen/schemas/legal_hold_policy_assigned_item.py b/box_sdk_gen/schemas/legal_hold_policy_assigned_item.py new file mode 100644 index 000000000..b1c950182 --- /dev/null +++ b/box_sdk_gen/schemas/legal_hold_policy_assigned_item.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.file import File + +from box_sdk_gen.schemas.folder import Folder + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.box.errors import BoxSDKError + +LegalHoldPolicyAssignedItem = Union[File, Folder, WebLink] diff --git a/box_sdk_gen/schemas/legal_hold_policy_assignment.py b/box_sdk_gen/schemas/legal_hold_policy_assignment.py new file mode 100644 index 000000000..3b00d1af6 --- /dev/null +++ b/box_sdk_gen/schemas/legal_hold_policy_assignment.py @@ -0,0 +1,63 @@ +from typing import Optional + +from box_sdk_gen.schemas.legal_hold_policy_assignment_base import ( + LegalHoldPolicyAssignmentBaseTypeField, +) + +from box_sdk_gen.schemas.file import File + +from box_sdk_gen.schemas.folder import Folder + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.legal_hold_policy_assignment_base import ( + LegalHoldPolicyAssignmentBase, +) + +from box_sdk_gen.schemas.legal_hold_policy_mini import LegalHoldPolicyMini + +from box_sdk_gen.schemas.legal_hold_policy_assigned_item import ( + LegalHoldPolicyAssignedItem, +) + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class LegalHoldPolicyAssignment(LegalHoldPolicyAssignmentBase): + def __init__( + self, + *, + legal_hold_policy: Optional[LegalHoldPolicyMini] = None, + assigned_to: Optional[LegalHoldPolicyAssignedItem] = None, + assigned_by: Optional[UserMini] = None, + assigned_at: Optional[DateTime] = None, + deleted_at: Optional[DateTime] = None, + id: Optional[str] = None, + type: Optional[LegalHoldPolicyAssignmentBaseTypeField] = None, + **kwargs + ): + """ + :param assigned_at: When the legal hold policy assignment object was + created., defaults to None + :type assigned_at: Optional[DateTime], optional + :param deleted_at: When the assignment release request was sent. + (Because it can take time for an assignment to fully + delete, this isn't quite the same time that the + assignment is fully deleted). If null, Assignment + was not deleted., defaults to None + :type deleted_at: Optional[DateTime], optional + :param id: The unique identifier for this legal hold assignment., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `legal_hold_policy_assignment`., defaults to None + :type type: Optional[LegalHoldPolicyAssignmentBaseTypeField], optional + """ + super().__init__(id=id, type=type, **kwargs) + self.legal_hold_policy = legal_hold_policy + self.assigned_to = assigned_to + self.assigned_by = assigned_by + self.assigned_at = assigned_at + self.deleted_at = deleted_at diff --git a/box_sdk_gen/schemas/legal_hold_policy_assignment_base.py b/box_sdk_gen/schemas/legal_hold_policy_assignment_base.py new file mode 100644 index 000000000..2e1897da7 --- /dev/null +++ b/box_sdk_gen/schemas/legal_hold_policy_assignment_base.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class LegalHoldPolicyAssignmentBaseTypeField(str, Enum): + LEGAL_HOLD_POLICY_ASSIGNMENT = 'legal_hold_policy_assignment' + + +class LegalHoldPolicyAssignmentBase(BaseObject): + _discriminator = 'type', {'legal_hold_policy_assignment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[LegalHoldPolicyAssignmentBaseTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this legal hold assignment., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `legal_hold_policy_assignment`., defaults to None + :type type: Optional[LegalHoldPolicyAssignmentBaseTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/legal_hold_policy_assignments.py b/box_sdk_gen/schemas/legal_hold_policy_assignments.py new file mode 100644 index 000000000..d66c7de7f --- /dev/null +++ b/box_sdk_gen/schemas/legal_hold_policy_assignments.py @@ -0,0 +1,39 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.legal_hold_policy_assignment import LegalHoldPolicyAssignment + +from box_sdk_gen.box.errors import BoxSDKError + + +class LegalHoldPolicyAssignments(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[LegalHoldPolicyAssignment]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of legal hold + policy assignments., defaults to None + :type entries: Optional[List[LegalHoldPolicyAssignment]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/legal_hold_policy_mini.py b/box_sdk_gen/schemas/legal_hold_policy_mini.py new file mode 100644 index 000000000..e2a82a406 --- /dev/null +++ b/box_sdk_gen/schemas/legal_hold_policy_mini.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class LegalHoldPolicyMiniTypeField(str, Enum): + LEGAL_HOLD_POLICY = 'legal_hold_policy' + + +class LegalHoldPolicyMini(BaseObject): + _discriminator = 'type', {'legal_hold_policy'} + + def __init__( + self, + id: str, + *, + type: LegalHoldPolicyMiniTypeField = LegalHoldPolicyMiniTypeField.LEGAL_HOLD_POLICY, + **kwargs + ): + """ + :param id: The unique identifier for this legal hold policy. + :type id: str + :param type: The value will always be `legal_hold_policy`., defaults to LegalHoldPolicyMiniTypeField.LEGAL_HOLD_POLICY + :type type: LegalHoldPolicyMiniTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/metadata.py b/box_sdk_gen/schemas/metadata.py new file mode 100644 index 000000000..1eee77e39 --- /dev/null +++ b/box_sdk_gen/schemas/metadata.py @@ -0,0 +1,36 @@ +from typing import Optional + +from box_sdk_gen.schemas.metadata_base import MetadataBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class Metadata(MetadataBase): + def __init__( + self, + *, + parent: Optional[str] = None, + template: Optional[str] = None, + scope: Optional[str] = None, + version: Optional[int] = None, + **kwargs + ): + """ + :param parent: The identifier of the item that this metadata instance + has been attached to. This combines the `type` and the `id` + of the parent in the form `{type}_{id}`., defaults to None + :type parent: Optional[str], optional + :param template: The name of the template., defaults to None + :type template: Optional[str], optional + :param scope: An ID for the scope in which this template + has been applied. This will be `enterprise_{enterprise_id}` for templates + defined for use in this enterprise, and `global` for general templates + that are available to all enterprises using Box., defaults to None + :type scope: Optional[str], optional + :param version: The version of the metadata instance. This version starts at 0 and + increases every time a user-defined property is modified., defaults to None + :type version: Optional[int], optional + """ + super().__init__( + parent=parent, template=template, scope=scope, version=version, **kwargs + ) diff --git a/box_sdk_gen/schemas/metadata_base.py b/box_sdk_gen/schemas/metadata_base.py new file mode 100644 index 000000000..f7173666a --- /dev/null +++ b/box_sdk_gen/schemas/metadata_base.py @@ -0,0 +1,55 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataBase(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'parent': '$parent', + 'template': '$template', + 'scope': '$scope', + 'version': '$version', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + '$parent': 'parent', + '$template': 'template', + '$scope': 'scope', + '$version': 'version', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + parent: Optional[str] = None, + template: Optional[str] = None, + scope: Optional[str] = None, + version: Optional[int] = None, + **kwargs + ): + """ + :param parent: The identifier of the item that this metadata instance + has been attached to. This combines the `type` and the `id` + of the parent in the form `{type}_{id}`., defaults to None + :type parent: Optional[str], optional + :param template: The name of the template., defaults to None + :type template: Optional[str], optional + :param scope: An ID for the scope in which this template + has been applied. This will be `enterprise_{enterprise_id}` for templates + defined for use in this enterprise, and `global` for general templates + that are available to all enterprises using Box., defaults to None + :type scope: Optional[str], optional + :param version: The version of the metadata instance. This version starts at 0 and + increases every time a user-defined property is modified., defaults to None + :type version: Optional[int], optional + """ + super().__init__(**kwargs) + self.parent = parent + self.template = template + self.scope = scope + self.version = version diff --git a/box_sdk_gen/schemas/metadata_cascade_policies.py b/box_sdk_gen/schemas/metadata_cascade_policies.py new file mode 100644 index 000000000..7c12ac0aa --- /dev/null +++ b/box_sdk_gen/schemas/metadata_cascade_policies.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.metadata_cascade_policy import MetadataCascadePolicy + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataCascadePolicies(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[MetadataCascadePolicy]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of metadata cascade policies., defaults to None + :type entries: Optional[List[MetadataCascadePolicy]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/metadata_cascade_policy.py b/box_sdk_gen/schemas/metadata_cascade_policy.py new file mode 100644 index 000000000..a3096866f --- /dev/null +++ b/box_sdk_gen/schemas/metadata_cascade_policy.py @@ -0,0 +1,126 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataCascadePolicyTypeField(str, Enum): + METADATA_CASCADE_POLICY = 'metadata_cascade_policy' + + +class MetadataCascadePolicyOwnerEnterpriseTypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class MetadataCascadePolicyOwnerEnterpriseField(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + type: Optional[MetadataCascadePolicyOwnerEnterpriseTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[MetadataCascadePolicyOwnerEnterpriseTypeField], optional + :param id: The ID of the enterprise that owns the policy., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class MetadataCascadePolicyParentTypeField(str, Enum): + FOLDER = 'folder' + + +class MetadataCascadePolicyParentField(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + *, + type: Optional[MetadataCascadePolicyParentTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `folder`., defaults to None + :type type: Optional[MetadataCascadePolicyParentTypeField], optional + :param id: The ID of the folder the policy is applied to., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class MetadataCascadePolicy(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'template_key': 'templateKey', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'templateKey': 'template_key', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'metadata_cascade_policy'} + + def __init__( + self, + id: str, + *, + type: MetadataCascadePolicyTypeField = MetadataCascadePolicyTypeField.METADATA_CASCADE_POLICY, + owner_enterprise: Optional[MetadataCascadePolicyOwnerEnterpriseField] = None, + parent: Optional[MetadataCascadePolicyParentField] = None, + scope: Optional[str] = None, + template_key: Optional[str] = None, + **kwargs + ): + """ + :param id: The ID of the metadata cascade policy object. + :type id: str + :param type: The value will always be `metadata_cascade_policy`., defaults to MetadataCascadePolicyTypeField.METADATA_CASCADE_POLICY + :type type: MetadataCascadePolicyTypeField, optional + :param owner_enterprise: The enterprise that owns this policy., defaults to None + :type owner_enterprise: Optional[MetadataCascadePolicyOwnerEnterpriseField], optional + :param parent: Represent the folder the policy is applied to., defaults to None + :type parent: Optional[MetadataCascadePolicyParentField], optional + :param scope: The scope of the metadata cascade policy can either be `global` or + `enterprise_*`. The `global` scope is used for policies that are + available to any Box enterprise. The `enterprise_*` scope represents + policies that have been created within a specific enterprise, where `*` + will be the ID of that enterprise., defaults to None + :type scope: Optional[str], optional + :param template_key: The key of the template that is cascaded down to the folder's + children. + + In many cases the template key is automatically derived + of its display name, for example `Contract Template` would + become `contractTemplate`. In some cases the creator of the + template will have provided its own template key. + + Please [list the templates for an enterprise][list], or + get all instances on a [file][file] or [folder][folder] + to inspect a template's key. + + [list]: e://get-metadata-templates-enterprise + [file]: e://get-files-id-metadata + [folder]: e://get-folders-id-metadata, defaults to None + :type template_key: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.owner_enterprise = owner_enterprise + self.parent = parent + self.scope = scope + self.template_key = template_key diff --git a/box_sdk_gen/schemas/metadata_error.py b/box_sdk_gen/schemas/metadata_error.py new file mode 100644 index 000000000..24d5ab170 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_error.py @@ -0,0 +1,29 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataError(BaseObject): + def __init__( + self, + *, + code: Optional[str] = None, + message: Optional[str] = None, + request_id: Optional[str] = None, + **kwargs + ): + """ + :param code: A Box-specific error code., defaults to None + :type code: Optional[str], optional + :param message: A short message describing the error., defaults to None + :type message: Optional[str], optional + :param request_id: A unique identifier for this response, which can be used + when contacting Box support., defaults to None + :type request_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.code = code + self.message = message + self.request_id = request_id diff --git a/box_sdk_gen/schemas/metadata_field_filter_date_range.py b/box_sdk_gen/schemas/metadata_field_filter_date_range.py new file mode 100644 index 000000000..8fd94463c --- /dev/null +++ b/box_sdk_gen/schemas/metadata_field_filter_date_range.py @@ -0,0 +1,28 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class MetadataFieldFilterDateRange(BaseObject): + def __init__( + self, *, lt: Optional[DateTime] = None, gt: Optional[DateTime] = None, **kwargs + ): + """ + :param lt: Specifies the (inclusive) upper bound for the metadata field + value. The value of a field must be lower than (`lt`) or + equal to this value for the search query to match this + template., defaults to None + :type lt: Optional[DateTime], optional + :param gt: Specifies the (inclusive) lower bound for the metadata field + value. The value of a field must be greater than (`gt`) or + equal to this value for the search query to match this + template., defaults to None + :type gt: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.lt = lt + self.gt = gt diff --git a/box_sdk_gen/schemas/metadata_field_filter_float_range.py b/box_sdk_gen/schemas/metadata_field_filter_float_range.py new file mode 100644 index 000000000..7e5d528c7 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_field_filter_float_range.py @@ -0,0 +1,26 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataFieldFilterFloatRange(BaseObject): + def __init__( + self, *, lt: Optional[float] = None, gt: Optional[float] = None, **kwargs + ): + """ + :param lt: Specifies the (inclusive) upper bound for the metadata field + value. The value of a field must be lower than (`lt`) or + equal to this value for the search query to match this + template., defaults to None + :type lt: Optional[float], optional + :param gt: Specifies the (inclusive) lower bound for the metadata field + value. The value of a field must be greater than (`gt`) or + equal to this value for the search query to match this + template., defaults to None + :type gt: Optional[float], optional + """ + super().__init__(**kwargs) + self.lt = lt + self.gt = gt diff --git a/box_sdk_gen/schemas/metadata_filter.py b/box_sdk_gen/schemas/metadata_filter.py new file mode 100644 index 000000000..e25881cf3 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_filter.py @@ -0,0 +1,77 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.metadata_field_filter_float_range import ( + MetadataFieldFilterFloatRange, +) + +from box_sdk_gen.schemas.metadata_field_filter_date_range import ( + MetadataFieldFilterDateRange, +) + +from box_sdk_gen.schemas.metadata_filter_value import MetadataFilterValue + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataFilterScopeField(str, Enum): + GLOBAL = 'global' + ENTERPRISE = 'enterprise' + ENTERPRISE__ENTERPRISE_ID_ = 'enterprise_{enterprise_id}' + + +class MetadataFilter(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'template_key': 'templateKey', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'templateKey': 'template_key', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + scope: Optional[MetadataFilterScopeField] = None, + template_key: Optional[str] = None, + filters: Optional[Dict[str, MetadataFilterValue]] = None, + **kwargs + ): + """ + :param scope: Specifies the scope of the template to filter search results by. + + This will be `enterprise_{enterprise_id}` for templates defined + for use in this enterprise, and `global` for general templates + that are available to all enterprises using Box., defaults to None + :type scope: Optional[MetadataFilterScopeField], optional + :param template_key: The key of the template used to filter search results. + + In many cases the template key is automatically derived + of its display name, for example `Contract Template` would + become `contractTemplate`. In some cases the creator of the + template will have provided its own template key. + + Please [list the templates for an enterprise][list], or + get all instances on a [file][file] or [folder][folder] + to inspect a template's key. + + [list]: e://get-metadata-templates-enterprise + [file]: e://get-files-id-metadata + [folder]: e://get-folders-id-metadata, defaults to None + :type template_key: Optional[str], optional + :param filters: Specifies which fields on the template to filter the search + results by. When more than one field is specified, the query + performs a logical `AND` to ensure that the instance of the + template matches each of the fields specified., defaults to None + :type filters: Optional[Dict[str, MetadataFilterValue]], optional + """ + super().__init__(**kwargs) + self.scope = scope + self.template_key = template_key + self.filters = filters diff --git a/box_sdk_gen/schemas/metadata_filter_value.py b/box_sdk_gen/schemas/metadata_filter_value.py new file mode 100644 index 000000000..91fbd65c2 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_filter_value.py @@ -0,0 +1,17 @@ +from typing import Union + +from typing import List + +from box_sdk_gen.schemas.metadata_field_filter_float_range import ( + MetadataFieldFilterFloatRange, +) + +from box_sdk_gen.schemas.metadata_field_filter_date_range import ( + MetadataFieldFilterDateRange, +) + +from box_sdk_gen.box.errors import BoxSDKError + +MetadataFilterValue = Union[ + str, float, List[str], MetadataFieldFilterFloatRange, MetadataFieldFilterDateRange +] diff --git a/box_sdk_gen/schemas/metadata_full.py b/box_sdk_gen/schemas/metadata_full.py new file mode 100644 index 000000000..4e52a463c --- /dev/null +++ b/box_sdk_gen/schemas/metadata_full.py @@ -0,0 +1,76 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.schemas.metadata_base import MetadataBase + +from box_sdk_gen.schemas.metadata import Metadata + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataFull(Metadata): + _fields_to_json_mapping: Dict[str, str] = { + 'can_edit': '$canEdit', + 'id': '$id', + 'type': '$type', + 'type_version': '$typeVersion', + **Metadata._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + '$canEdit': 'can_edit', + '$id': 'id', + '$type': 'type', + '$typeVersion': 'type_version', + **Metadata._json_to_fields_mapping, + } + + def __init__( + self, + *, + can_edit: Optional[bool] = None, + id: Optional[str] = None, + type: Optional[str] = None, + type_version: Optional[int] = None, + parent: Optional[str] = None, + template: Optional[str] = None, + scope: Optional[str] = None, + version: Optional[int] = None, + **kwargs + ): + """ + :param can_edit: Whether the user can edit this metadata instance., defaults to None + :type can_edit: Optional[bool], optional + :param id: A UUID to identify the metadata instance., defaults to None + :type id: Optional[str], optional + :param type: A unique identifier for the "type" of this instance. This is an + internal system property and should not be used by a client + application., defaults to None + :type type: Optional[str], optional + :param type_version: The last-known version of the template of the object. This is an + internal system property and should not be used by a client + application., defaults to None + :type type_version: Optional[int], optional + :param parent: The identifier of the item that this metadata instance + has been attached to. This combines the `type` and the `id` + of the parent in the form `{type}_{id}`., defaults to None + :type parent: Optional[str], optional + :param template: The name of the template., defaults to None + :type template: Optional[str], optional + :param scope: An ID for the scope in which this template + has been applied. This will be `enterprise_{enterprise_id}` for templates + defined for use in this enterprise, and `global` for general templates + that are available to all enterprises using Box., defaults to None + :type scope: Optional[str], optional + :param version: The version of the metadata instance. This version starts at 0 and + increases every time a user-defined property is modified., defaults to None + :type version: Optional[int], optional + """ + super().__init__( + parent=parent, template=template, scope=scope, version=version, **kwargs + ) + self.can_edit = can_edit + self.id = id + self.type = type + self.type_version = type_version + self.extra_data = kwargs diff --git a/box_sdk_gen/schemas/metadata_instance_value.py b/box_sdk_gen/schemas/metadata_instance_value.py new file mode 100644 index 000000000..89af11f7b --- /dev/null +++ b/box_sdk_gen/schemas/metadata_instance_value.py @@ -0,0 +1,7 @@ +from typing import Union + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + +MetadataInstanceValue = Union[str, int, float, List[str]] diff --git a/box_sdk_gen/schemas/metadata_query.py b/box_sdk_gen/schemas/metadata_query.py new file mode 100644 index 000000000..e98c90b51 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_query.py @@ -0,0 +1,129 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Dict + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataQueryOrderByDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class MetadataQueryOrderByField(BaseObject): + def __init__( + self, + *, + field_key: Optional[str] = None, + direction: Optional[MetadataQueryOrderByDirectionField] = None, + **kwargs + ): + """ + :param field_key: The metadata template field to order by. + + The `field_key` represents the `key` value of a field from the + metadata template being searched for., defaults to None + :type field_key: Optional[str], optional + :param direction: The direction to order by, either ascending or descending. + + The `ordering` direction must be the same for each item in the + array., defaults to None + :type direction: Optional[MetadataQueryOrderByDirectionField], optional + """ + super().__init__(**kwargs) + self.field_key = field_key + self.direction = direction + + +class MetadataQuery(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'from_': 'from', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'from': 'from_', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + from_: str, + ancestor_folder_id: str, + *, + query: Optional[str] = None, + query_params: Optional[Dict] = None, + order_by: Optional[List[MetadataQueryOrderByField]] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Optional[List[str]] = None, + **kwargs + ): + """ + :param from_: Specifies the template used in the query. Must be in the form + `scope.templateKey`. Not all templates can be used in this field, + most notably the built-in, Box-provided classification templates + can not be used in a query. + :type from_: str + :param ancestor_folder_id: The ID of the folder that you are restricting the query to. A + value of zero will return results from all folders you have access + to. A non-zero value will only return results found in the folder + corresponding to the ID or in any of its subfolders. + :type ancestor_folder_id: str + :param query: The query to perform. A query is a logical expression that is very similar + to a SQL `SELECT` statement. Values in the search query can be turned into + parameters specified in the `query_param` arguments list to prevent having + to manually insert search values into the query string. + + For example, a value of `:amount` would represent the `amount` value in + `query_params` object., defaults to None + :type query: Optional[str], optional + :param query_params: Set of arguments corresponding to the parameters specified in the + `query`. The type of each parameter used in the `query_params` must match + the type of the corresponding metadata template field., defaults to None + :type query_params: Optional[Dict], optional + :param order_by: A list of template fields and directions to sort the metadata query + results by. + + The ordering `direction` must be the same for each item in the array., defaults to None + :type order_by: Optional[List[MetadataQueryOrderByField]], optional + :param limit: A value between 0 and 100 that indicates the maximum number of results + to return for a single request. This only specifies a maximum + boundary and will not guarantee the minimum number of results + returned., defaults to None + :type limit: Optional[int], optional + :param marker: Marker to use for requesting the next page., defaults to None + :type marker: Optional[str], optional + :param fields: By default, this endpoint returns only the most basic info about the items for + which the query matches. This attribute can be used to specify a list of + additional attributes to return for any item, including its metadata. + + This attribute takes a list of item fields, metadata template identifiers, + or metadata template field identifiers. + + For example: + + * `created_by` will add the details of the user who created the item to + the response. + * `metadata..` will return the mini-representation + of the metadata instance identified by the `scope` and `templateKey`. + * `metadata...` will return all the mini-representation + of the metadata instance identified by the `scope` and `templateKey` plus + the field specified by the `field` name. Multiple fields for the same + `scope` and `templateKey` can be defined., defaults to None + :type fields: Optional[List[str]], optional + """ + super().__init__(**kwargs) + self.from_ = from_ + self.ancestor_folder_id = ancestor_folder_id + self.query = query + self.query_params = query_params + self.order_by = order_by + self.limit = limit + self.marker = marker + self.fields = fields diff --git a/box_sdk_gen/schemas/metadata_query_result_item.py b/box_sdk_gen/schemas/metadata_query_result_item.py new file mode 100644 index 000000000..a2425d816 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_query_result_item.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.box.errors import BoxSDKError + +MetadataQueryResultItem = Union[FileFull, FolderFull] diff --git a/box_sdk_gen/schemas/metadata_query_results.py b/box_sdk_gen/schemas/metadata_query_results.py new file mode 100644 index 000000000..570e918e6 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_query_results.py @@ -0,0 +1,43 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.metadata_query_result_item import MetadataQueryResultItem + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataQueryResults(BaseObject): + def __init__( + self, + *, + entries: Optional[List[MetadataQueryResultItem]] = None, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: The mini representation of the files and folders that match the search + terms. + + By default, this endpoint returns only the most basic info about the + items. To get additional fields for each item, including any of the + metadata, use the `fields` attribute in the query., defaults to None + :type entries: Optional[List[MetadataQueryResultItem]], optional + :param limit: The limit that was used for this search. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/metadata_template.py b/box_sdk_gen/schemas/metadata_template.py new file mode 100644 index 000000000..3596a105f --- /dev/null +++ b/box_sdk_gen/schemas/metadata_template.py @@ -0,0 +1,175 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataTemplateTypeField(str, Enum): + METADATA_TEMPLATE = 'metadata_template' + + +class MetadataTemplateFieldsTypeField(str, Enum): + STRING = 'string' + FLOAT = 'float' + DATE = 'date' + ENUM = 'enum' + MULTISELECT = 'multiSelect' + INTEGER = 'integer' + + +class MetadataTemplateFieldsOptionsField(BaseObject): + def __init__(self, key: str, *, id: Optional[str] = None, **kwargs): + """ + :param key: The text value of the option. This represents both the display name of the + option and the internal key used when updating templates. + :type key: str + :param id: The internal unique identifier of the option., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.key = key + self.id = id + + +class MetadataTemplateFieldsField(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'display_name': 'displayName', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'displayName': 'display_name', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', { + 'string', + 'float', + 'date', + 'enum', + 'multiSelect', + 'integer', + } + + def __init__( + self, + type: MetadataTemplateFieldsTypeField, + key: str, + display_name: str, + *, + description: Optional[str] = None, + hidden: Optional[bool] = None, + options: Optional[List[MetadataTemplateFieldsOptionsField]] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of field. The basic fields are a `string` field for text, a + `float` field for numbers, and a `date` fields to present the user with a + date-time picker. + + Additionally, metadata templates support an `enum` field for a basic list + of items, and ` multiSelect` field for a similar list of items where the + user can select more than one value. + + **Note**: The `integer` value is deprecated. + It is still present in the response, + but cannot be used in the POST request. + :type type: MetadataTemplateFieldsTypeField + :param key: A unique identifier for the field. The identifier must + be unique within the template to which it belongs. + :type key: str + :param display_name: The display name of the field as it is shown to the user in the web and + mobile apps. + :type display_name: str + :param description: A description of the field. This is not shown to the user., defaults to None + :type description: Optional[str], optional + :param hidden: Whether this field is hidden in the UI for the user and can only be set + through the API instead., defaults to None + :type hidden: Optional[bool], optional + :param options: A list of options for this field. This is used in combination + with the `enum` and `multiSelect` field types., defaults to None + :type options: Optional[List[MetadataTemplateFieldsOptionsField]], optional + :param id: The unique ID of the metadata template field., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.key = key + self.display_name = display_name + self.description = description + self.hidden = hidden + self.options = options + self.id = id + + +class MetadataTemplate(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'template_key': 'templateKey', + 'display_name': 'displayName', + 'copy_instance_on_item_copy': 'copyInstanceOnItemCopy', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'templateKey': 'template_key', + 'displayName': 'display_name', + 'copyInstanceOnItemCopy': 'copy_instance_on_item_copy', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'metadata_template'} + + def __init__( + self, + id: str, + *, + type: MetadataTemplateTypeField = MetadataTemplateTypeField.METADATA_TEMPLATE, + scope: Optional[str] = None, + template_key: Optional[str] = None, + display_name: Optional[str] = None, + hidden: Optional[bool] = None, + fields: Optional[List[MetadataTemplateFieldsField]] = None, + copy_instance_on_item_copy: Optional[bool] = None, + **kwargs + ): + """ + :param id: The ID of the metadata template. + :type id: str + :param type: The value will always be `metadata_template`., defaults to MetadataTemplateTypeField.METADATA_TEMPLATE + :type type: MetadataTemplateTypeField, optional + :param scope: The scope of the metadata template can either be `global` or + `enterprise_*`. The `global` scope is used for templates that are + available to any Box enterprise. The `enterprise_*` scope represents + templates that have been created within a specific enterprise, where `*` + will be the ID of that enterprise., defaults to None + :type scope: Optional[str], optional + :param template_key: A unique identifier for the template. This identifier is unique across + the `scope` of the enterprise to which the metadata template is being + applied, yet is not necessarily unique across different enterprises., defaults to None + :type template_key: Optional[str], optional + :param display_name: The display name of the template. This can be seen in the Box web app + and mobile apps., defaults to None + :type display_name: Optional[str], optional + :param hidden: Defines if this template is visible in the Box web app UI, or if + it is purely intended for usage through the API., defaults to None + :type hidden: Optional[bool], optional + :param fields: An ordered list of template fields which are part of the template. Each + field can be a regular text field, date field, number field, as well as a + single or multi-select list., defaults to None + :type fields: Optional[List[MetadataTemplateFieldsField]], optional + :param copy_instance_on_item_copy: Whether or not to include the metadata when a file or folder is copied., defaults to None + :type copy_instance_on_item_copy: Optional[bool], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.scope = scope + self.template_key = template_key + self.display_name = display_name + self.hidden = hidden + self.fields = fields + self.copy_instance_on_item_copy = copy_instance_on_item_copy diff --git a/box_sdk_gen/schemas/metadata_templates.py b/box_sdk_gen/schemas/metadata_templates.py new file mode 100644 index 000000000..f0f8b6fa7 --- /dev/null +++ b/box_sdk_gen/schemas/metadata_templates.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.metadata_template import MetadataTemplate + +from box_sdk_gen.box.errors import BoxSDKError + + +class MetadataTemplates(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[MetadataTemplate]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of metadata templates., defaults to None + :type entries: Optional[List[MetadataTemplate]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/metadatas.py b/box_sdk_gen/schemas/metadatas.py new file mode 100644 index 000000000..428f4cf6c --- /dev/null +++ b/box_sdk_gen/schemas/metadatas.py @@ -0,0 +1,28 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.metadata import Metadata + +from box_sdk_gen.box.errors import BoxSDKError + + +class Metadatas(BaseObject): + def __init__( + self, + *, + entries: Optional[List[Metadata]] = None, + limit: Optional[int] = None, + **kwargs + ): + """ + :param entries: A list of metadata instances, as applied to this file or folder., defaults to None + :type entries: Optional[List[Metadata]], optional + :param limit: The limit that was used for this page of results., defaults to None + :type limit: Optional[int], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit diff --git a/box_sdk_gen/schemas/o_auth_2_error.py b/box_sdk_gen/schemas/o_auth_2_error.py new file mode 100644 index 000000000..524fe4f20 --- /dev/null +++ b/box_sdk_gen/schemas/o_auth_2_error.py @@ -0,0 +1,24 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class OAuth2Error(BaseObject): + def __init__( + self, + *, + error: Optional[str] = None, + error_description: Optional[str] = None, + **kwargs + ): + """ + :param error: The type of the error returned., defaults to None + :type error: Optional[str], optional + :param error_description: The type of the error returned., defaults to None + :type error_description: Optional[str], optional + """ + super().__init__(**kwargs) + self.error = error + self.error_description = error_description diff --git a/box_sdk_gen/schemas/outcome.py b/box_sdk_gen/schemas/outcome.py new file mode 100644 index 000000000..e0060eb42 --- /dev/null +++ b/box_sdk_gen/schemas/outcome.py @@ -0,0 +1,36 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.collaborator_variable import CollaboratorVariable + +from box_sdk_gen.schemas.completion_rule_variable import CompletionRuleVariable + +from box_sdk_gen.schemas.role_variable import RoleVariable + +from box_sdk_gen.box.errors import BoxSDKError + + +class Outcome(BaseObject): + def __init__( + self, + id: str, + *, + collaborators: Optional[CollaboratorVariable] = None, + completion_rule: Optional[CompletionRuleVariable] = None, + file_collaborator_role: Optional[RoleVariable] = None, + task_collaborators: Optional[CollaboratorVariable] = None, + role: Optional[RoleVariable] = None, + **kwargs + ): + """ + :param id: ID of a specific outcome. + :type id: str + """ + super().__init__(**kwargs) + self.id = id + self.collaborators = collaborators + self.completion_rule = completion_rule + self.file_collaborator_role = file_collaborator_role + self.task_collaborators = task_collaborators + self.role = role diff --git a/box_sdk_gen/schemas/post_o_auth_2_revoke.py b/box_sdk_gen/schemas/post_o_auth_2_revoke.py new file mode 100644 index 000000000..d93eec3c4 --- /dev/null +++ b/box_sdk_gen/schemas/post_o_auth_2_revoke.py @@ -0,0 +1,30 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class PostOAuth2Revoke(BaseObject): + def __init__( + self, + *, + client_id: Optional[str] = None, + client_secret: Optional[str] = None, + token: Optional[str] = None, + **kwargs + ): + """ + :param client_id: The Client ID of the application requesting to revoke the + access token., defaults to None + :type client_id: Optional[str], optional + :param client_secret: The client secret of the application requesting to revoke + an access token., defaults to None + :type client_secret: Optional[str], optional + :param token: The access token to revoke., defaults to None + :type token: Optional[str], optional + """ + super().__init__(**kwargs) + self.client_id = client_id + self.client_secret = client_secret + self.token = token diff --git a/box_sdk_gen/schemas/post_o_auth_2_token.py b/box_sdk_gen/schemas/post_o_auth_2_token.py new file mode 100644 index 000000000..1d92c4c54 --- /dev/null +++ b/box_sdk_gen/schemas/post_o_auth_2_token.py @@ -0,0 +1,146 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class PostOAuth2TokenGrantTypeField(str, Enum): + AUTHORIZATION_CODE = 'authorization_code' + REFRESH_TOKEN = 'refresh_token' + CLIENT_CREDENTIALS = 'client_credentials' + URN_IETF_PARAMS_OAUTH_GRANT_TYPE_JWT_BEARER = ( + 'urn:ietf:params:oauth:grant-type:jwt-bearer' + ) + URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE = ( + 'urn:ietf:params:oauth:grant-type:token-exchange' + ) + + +class PostOAuth2TokenSubjectTokenTypeField(str, Enum): + URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN = ( + 'urn:ietf:params:oauth:token-type:access_token' + ) + + +class PostOAuth2TokenActorTokenTypeField(str, Enum): + URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ID_TOKEN = ( + 'urn:ietf:params:oauth:token-type:id_token' + ) + + +class PostOAuth2TokenBoxSubjectTypeField(str, Enum): + ENTERPRISE = 'enterprise' + USER = 'user' + + +class PostOAuth2Token(BaseObject): + def __init__( + self, + grant_type: PostOAuth2TokenGrantTypeField, + *, + client_id: Optional[str] = None, + client_secret: Optional[str] = None, + code: Optional[str] = None, + refresh_token: Optional[str] = None, + assertion: Optional[str] = None, + subject_token: Optional[str] = None, + subject_token_type: Optional[PostOAuth2TokenSubjectTokenTypeField] = None, + actor_token: Optional[str] = None, + actor_token_type: Optional[PostOAuth2TokenActorTokenTypeField] = None, + scope: Optional[str] = None, + resource: Optional[str] = None, + box_subject_type: Optional[PostOAuth2TokenBoxSubjectTypeField] = None, + box_subject_id: Optional[str] = None, + box_shared_link: Optional[str] = None, + **kwargs + ): + """ + :param grant_type: The type of request being made, either using a client-side obtained + authorization code, a refresh token, a JWT assertion, client credentials + grant or another access token for the purpose of downscoping a token. + :type grant_type: PostOAuth2TokenGrantTypeField + :param client_id: The Client ID of the application requesting an access token. + + Used in combination with `authorization_code`, `client_credentials`, or + `urn:ietf:params:oauth:grant-type:jwt-bearer` as the `grant_type`., defaults to None + :type client_id: Optional[str], optional + :param client_secret: The client secret of the application requesting an access token. + + Used in combination with `authorization_code`, `client_credentials`, or + `urn:ietf:params:oauth:grant-type:jwt-bearer` as the `grant_type`., defaults to None + :type client_secret: Optional[str], optional + :param code: The client-side authorization code passed to your application by + Box in the browser redirect after the user has successfully + granted your application permission to make API calls on their + behalf. + + Used in combination with `authorization_code` as the `grant_type`., defaults to None + :type code: Optional[str], optional + :param refresh_token: A refresh token used to get a new access token with. + + Used in combination with `refresh_token` as the `grant_type`., defaults to None + :type refresh_token: Optional[str], optional + :param assertion: A JWT assertion for which to request a new access token. + + Used in combination with `urn:ietf:params:oauth:grant-type:jwt-bearer` + as the `grant_type`., defaults to None + :type assertion: Optional[str], optional + :param subject_token: The token to exchange for a downscoped token. This can be a regular + access token, a JWT assertion, or an app token. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type subject_token: Optional[str], optional + :param subject_token_type: The type of `subject_token` passed in. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type subject_token_type: Optional[PostOAuth2TokenSubjectTokenTypeField], optional + :param actor_token: The token used to create an annotator token. + This is a JWT assertion. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type actor_token: Optional[str], optional + :param actor_token_type: The type of `actor_token` passed in. + + Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` + as the `grant_type`., defaults to None + :type actor_token_type: Optional[PostOAuth2TokenActorTokenTypeField], optional + :param scope: The space-delimited list of scopes that you want apply to the + new access token. + + The `subject_token` will need to have all of these scopes or + the call will error with **401 Unauthorized**.., defaults to None + :type scope: Optional[str], optional + :param resource: Full URL for the file that the token should be generated for., defaults to None + :type resource: Optional[str], optional + :param box_subject_type: Used in combination with `client_credentials` as the `grant_type`., defaults to None + :type box_subject_type: Optional[PostOAuth2TokenBoxSubjectTypeField], optional + :param box_subject_id: Used in combination with `client_credentials` as the `grant_type`. + Value is determined by `box_subject_type`. If `user` use user ID and if + `enterprise` use enterprise ID., defaults to None + :type box_subject_id: Optional[str], optional + :param box_shared_link: Full URL of the shared link on the file or folder + that the token should be generated for., defaults to None + :type box_shared_link: Optional[str], optional + """ + super().__init__(**kwargs) + self.grant_type = grant_type + self.client_id = client_id + self.client_secret = client_secret + self.code = code + self.refresh_token = refresh_token + self.assertion = assertion + self.subject_token = subject_token + self.subject_token_type = subject_token_type + self.actor_token = actor_token + self.actor_token_type = actor_token_type + self.scope = scope + self.resource = resource + self.box_subject_type = box_subject_type + self.box_subject_id = box_subject_id + self.box_shared_link = box_shared_link diff --git a/box_sdk_gen/schemas/post_o_auth_2_token_refresh_access_token.py b/box_sdk_gen/schemas/post_o_auth_2_token_refresh_access_token.py new file mode 100644 index 000000000..a6eb8c5e3 --- /dev/null +++ b/box_sdk_gen/schemas/post_o_auth_2_token_refresh_access_token.py @@ -0,0 +1,36 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class PostOAuth2TokenRefreshAccessTokenGrantTypeField(str, Enum): + REFRESH_TOKEN = 'refresh_token' + + +class PostOAuth2TokenRefreshAccessToken(BaseObject): + def __init__( + self, + client_id: str, + client_secret: str, + refresh_token: str, + *, + grant_type: PostOAuth2TokenRefreshAccessTokenGrantTypeField = PostOAuth2TokenRefreshAccessTokenGrantTypeField.REFRESH_TOKEN, + **kwargs + ): + """ + :param client_id: The client ID of the application requesting to refresh the token. + :type client_id: str + :param client_secret: The client secret of the application requesting to refresh the token. + :type client_secret: str + :param refresh_token: The refresh token to refresh. + :type refresh_token: str + :param grant_type: The type of request being made, in this case a refresh request., defaults to PostOAuth2TokenRefreshAccessTokenGrantTypeField.REFRESH_TOKEN + :type grant_type: PostOAuth2TokenRefreshAccessTokenGrantTypeField, optional + """ + super().__init__(**kwargs) + self.client_id = client_id + self.client_secret = client_secret + self.refresh_token = refresh_token + self.grant_type = grant_type diff --git a/box_sdk_gen/schemas/realtime_server.py b/box_sdk_gen/schemas/realtime_server.py new file mode 100644 index 000000000..f8228268e --- /dev/null +++ b/box_sdk_gen/schemas/realtime_server.py @@ -0,0 +1,43 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class RealtimeServer(BaseObject): + def __init__( + self, + *, + type: Optional[str] = None, + url: Optional[str] = None, + ttl: Optional[str] = None, + max_retries: Optional[str] = None, + retry_timeout: Optional[int] = None, + **kwargs + ): + """ + :param type: The value will always be `realtime_server`., defaults to None + :type type: Optional[str], optional + :param url: The URL for the server., defaults to None + :type url: Optional[str], optional + :param ttl: The time in minutes for which this server is available., defaults to None + :type ttl: Optional[str], optional + :param max_retries: The maximum number of retries this server will + allow before a new long poll should be started by + getting a [new list of server](#options-events)., defaults to None + :type max_retries: Optional[str], optional + :param retry_timeout: The maximum number of seconds without a response + after which you should retry the long poll connection. + + This helps to overcome network issues where the long + poll looks to be working but no packages are coming + through., defaults to None + :type retry_timeout: Optional[int], optional + """ + super().__init__(**kwargs) + self.type = type + self.url = url + self.ttl = ttl + self.max_retries = max_retries + self.retry_timeout = retry_timeout diff --git a/box_sdk_gen/schemas/realtime_servers.py b/box_sdk_gen/schemas/realtime_servers.py new file mode 100644 index 000000000..933fb5c52 --- /dev/null +++ b/box_sdk_gen/schemas/realtime_servers.py @@ -0,0 +1,28 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.realtime_server import RealtimeServer + +from box_sdk_gen.box.errors import BoxSDKError + + +class RealtimeServers(BaseObject): + def __init__( + self, + *, + chunk_size: Optional[int] = None, + entries: Optional[List[RealtimeServer]] = None, + **kwargs + ): + """ + :param chunk_size: The number of items in this response., defaults to None + :type chunk_size: Optional[int], optional + :param entries: A list of real-time servers., defaults to None + :type entries: Optional[List[RealtimeServer]], optional + """ + super().__init__(**kwargs) + self.chunk_size = chunk_size + self.entries = entries diff --git a/box_sdk_gen/schemas/recent_item.py b/box_sdk_gen/schemas/recent_item.py new file mode 100644 index 000000000..38b7ebc20 --- /dev/null +++ b/box_sdk_gen/schemas/recent_item.py @@ -0,0 +1,56 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.recent_item_resource import RecentItemResource + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class RecentItemInteractionTypeField(str, Enum): + ITEM_PREVIEW = 'item_preview' + ITEM_UPLOAD = 'item_upload' + ITEM_COMMENT = 'item_comment' + ITEM_OPEN = 'item_open' + ITEM_MODIFY = 'item_modify' + + +class RecentItem(BaseObject): + def __init__( + self, + *, + type: Optional[str] = None, + item: Optional[RecentItemResource] = None, + interaction_type: Optional[RecentItemInteractionTypeField] = None, + interacted_at: Optional[DateTime] = None, + interaction_shared_link: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `recent_item`., defaults to None + :type type: Optional[str], optional + :param interaction_type: The most recent type of access the user performed on + the item., defaults to None + :type interaction_type: Optional[RecentItemInteractionTypeField], optional + :param interacted_at: The time of the most recent interaction., defaults to None + :type interacted_at: Optional[DateTime], optional + :param interaction_shared_link: If the item was accessed through a shared link it will appear here, + otherwise this will be null., defaults to None + :type interaction_shared_link: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.item = item + self.interaction_type = interaction_type + self.interacted_at = interacted_at + self.interaction_shared_link = interaction_shared_link diff --git a/box_sdk_gen/schemas/recent_item_resource.py b/box_sdk_gen/schemas/recent_item_resource.py new file mode 100644 index 000000000..2c707a676 --- /dev/null +++ b/box_sdk_gen/schemas/recent_item_resource.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.box.errors import BoxSDKError + +RecentItemResource = Union[FileFull, FolderFull, WebLink] diff --git a/box_sdk_gen/schemas/recent_items.py b/box_sdk_gen/schemas/recent_items.py new file mode 100644 index 000000000..8498a8fb9 --- /dev/null +++ b/box_sdk_gen/schemas/recent_items.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.recent_item import RecentItem + +from box_sdk_gen.box.errors import BoxSDKError + + +class RecentItems(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[RecentItem]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of recent items., defaults to None + :type entries: Optional[List[RecentItem]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/resource.py b/box_sdk_gen/schemas/resource.py new file mode 100644 index 000000000..eef77606d --- /dev/null +++ b/box_sdk_gen/schemas/resource.py @@ -0,0 +1,9 @@ +from typing import Union + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.box.errors import BoxSDKError + +Resource = Union[FolderMini, FileMini] diff --git a/box_sdk_gen/schemas/resource_scope.py b/box_sdk_gen/schemas/resource_scope.py new file mode 100644 index 000000000..9f0d71874 --- /dev/null +++ b/box_sdk_gen/schemas/resource_scope.py @@ -0,0 +1,47 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.resource import Resource + +from box_sdk_gen.box.errors import BoxSDKError + + +class ResourceScopeScopeField(str, Enum): + ANNOTATION_EDIT = 'annotation_edit' + ANNOTATION_VIEW_ALL = 'annotation_view_all' + ANNOTATION_VIEW_SELF = 'annotation_view_self' + BASE_EXPLORER = 'base_explorer' + BASE_PICKER = 'base_picker' + BASE_PREVIEW = 'base_preview' + BASE_UPLOAD = 'base_upload' + ITEM_DELETE = 'item_delete' + ITEM_DOWNLOAD = 'item_download' + ITEM_PREVIEW = 'item_preview' + ITEM_RENAME = 'item_rename' + ITEM_SHARE = 'item_share' + ITEM_UPLOAD = 'item_upload' + ITEM_READ = 'item_read' + + +class ResourceScope(BaseObject): + def __init__( + self, + *, + scope: Optional[ResourceScopeScopeField] = None, + object: Optional[Resource] = None, + **kwargs + ): + """ + :param scope: The scopes for the resource access., defaults to None + :type scope: Optional[ResourceScopeScopeField], optional + """ + super().__init__(**kwargs) + self.scope = scope + self.object = object diff --git a/box_sdk_gen/schemas/retention_policies.py b/box_sdk_gen/schemas/retention_policies.py new file mode 100644 index 000000000..3a365edc3 --- /dev/null +++ b/box_sdk_gen/schemas/retention_policies.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.retention_policy import RetentionPolicy + +from box_sdk_gen.box.errors import BoxSDKError + + +class RetentionPolicies(BaseObject): + def __init__( + self, + *, + entries: Optional[List[RetentionPolicy]] = None, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: A list in which each entry represents a retention policy object., defaults to None + :type entries: Optional[List[RetentionPolicy]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/retention_policy.py b/box_sdk_gen/schemas/retention_policy.py new file mode 100644 index 000000000..91f884090 --- /dev/null +++ b/box_sdk_gen/schemas/retention_policy.py @@ -0,0 +1,173 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.retention_policy_base import RetentionPolicyBaseTypeField + +from box_sdk_gen.schemas.retention_policy_base import RetentionPolicyBase + +from box_sdk_gen.schemas.retention_policy_mini import ( + RetentionPolicyMiniDispositionActionField, +) + +from box_sdk_gen.schemas.retention_policy_mini import RetentionPolicyMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class RetentionPolicyPolicyTypeField(str, Enum): + FINITE = 'finite' + INDEFINITE = 'indefinite' + + +class RetentionPolicyRetentionTypeField(str, Enum): + MODIFIABLE = 'modifiable' + NON_MODIFIABLE = 'non_modifiable' + + +class RetentionPolicyStatusField(str, Enum): + ACTIVE = 'active' + RETIRED = 'retired' + + +class RetentionPolicyAssignmentCountsField(BaseObject): + def __init__( + self, + *, + enterprise: Optional[int] = None, + folder: Optional[int] = None, + metadata_template: Optional[int] = None, + **kwargs + ): + """ + :param enterprise: The number of enterprise assignments this policy has. The maximum value is 1., defaults to None + :type enterprise: Optional[int], optional + :param folder: The number of folder assignments this policy has., defaults to None + :type folder: Optional[int], optional + :param metadata_template: The number of metadata template assignments this policy has., defaults to None + :type metadata_template: Optional[int], optional + """ + super().__init__(**kwargs) + self.enterprise = enterprise + self.folder = folder + self.metadata_template = metadata_template + + +class RetentionPolicy(RetentionPolicyMini): + def __init__( + self, + id: str, + *, + description: Optional[str] = None, + policy_type: Optional[RetentionPolicyPolicyTypeField] = None, + retention_type: Optional[RetentionPolicyRetentionTypeField] = None, + status: Optional[RetentionPolicyStatusField] = None, + created_by: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + can_owner_extend_retention: Optional[bool] = None, + are_owners_notified: Optional[bool] = None, + custom_notification_recipients: Optional[List[UserMini]] = None, + assignment_counts: Optional[RetentionPolicyAssignmentCountsField] = None, + policy_name: Optional[str] = None, + retention_length: Optional[str] = None, + disposition_action: Optional[RetentionPolicyMiniDispositionActionField] = None, + type: RetentionPolicyBaseTypeField = RetentionPolicyBaseTypeField.RETENTION_POLICY, + **kwargs + ): + """ + :param id: The unique identifier that represents a retention policy. + :type id: str + :param description: The additional text description of the retention policy., defaults to None + :type description: Optional[str], optional + :param policy_type: The type of the retention policy. A retention + policy type can either be `finite`, where a + specific amount of time to retain the content is known + upfront, or `indefinite`, where the amount of time + to retain the content is still unknown., defaults to None + :type policy_type: Optional[RetentionPolicyPolicyTypeField], optional + :param retention_type: Specifies the retention type: + + * `modifiable`: You can modify the retention policy. For example, + you can add or remove folders, shorten or lengthen + the policy duration, or delete the assignment. + Use this type if your retention policy + is not related to any regulatory purposes. + + * `non-modifiable`: You can modify the retention policy + only in a limited way: add a folder, lengthen the duration, + retire the policy, change the disposition action + or notification settings. You cannot perform other actions, + such as deleting the assignment or shortening the + policy duration. Use this type to ensure + compliance with regulatory retention policies., defaults to None + :type retention_type: Optional[RetentionPolicyRetentionTypeField], optional + :param status: The status of the retention policy. The status of + a policy will be `active`, unless explicitly retired by an + administrator, in which case the status will be `retired`. + Once a policy has been retired, it cannot become + active again., defaults to None + :type status: Optional[RetentionPolicyStatusField], optional + :param created_at: When the retention policy object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the retention policy object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param can_owner_extend_retention: Determines if the owner of items under the policy + can extend the retention when the original + retention duration is about to end., defaults to None + :type can_owner_extend_retention: Optional[bool], optional + :param are_owners_notified: Determines if owners and co-owners of items + under the policy are notified when + the retention duration is about to end., defaults to None + :type are_owners_notified: Optional[bool], optional + :param custom_notification_recipients: A list of users notified when the retention policy duration is about to end., defaults to None + :type custom_notification_recipients: Optional[List[UserMini]], optional + :param assignment_counts: Counts the retention policy assignments for each item type., defaults to None + :type assignment_counts: Optional[RetentionPolicyAssignmentCountsField], optional + :param policy_name: The name given to the retention policy., defaults to None + :type policy_name: Optional[str], optional + :param retention_length: The length of the retention policy. This value + specifies the duration in days that the retention + policy will be active for after being assigned to + content. If the policy has a `policy_type` of + `indefinite`, the `retention_length` will also be + `indefinite`., defaults to None + :type retention_length: Optional[str], optional + :param disposition_action: The disposition action of the retention policy. + This action can be `permanently_delete`, which + will cause the content retained by the policy + to be permanently deleted, or `remove_retention`, + which will lift the retention policy from the content, + allowing it to be deleted by users, + once the retention policy has expired., defaults to None + :type disposition_action: Optional[RetentionPolicyMiniDispositionActionField], optional + :param type: The value will always be `retention_policy`., defaults to RetentionPolicyBaseTypeField.RETENTION_POLICY + :type type: RetentionPolicyBaseTypeField, optional + """ + super().__init__( + id=id, + policy_name=policy_name, + retention_length=retention_length, + disposition_action=disposition_action, + type=type, + **kwargs + ) + self.description = description + self.policy_type = policy_type + self.retention_type = retention_type + self.status = status + self.created_by = created_by + self.created_at = created_at + self.modified_at = modified_at + self.can_owner_extend_retention = can_owner_extend_retention + self.are_owners_notified = are_owners_notified + self.custom_notification_recipients = custom_notification_recipients + self.assignment_counts = assignment_counts diff --git a/box_sdk_gen/schemas/retention_policy_assignment.py b/box_sdk_gen/schemas/retention_policy_assignment.py new file mode 100644 index 000000000..a1e492256 --- /dev/null +++ b/box_sdk_gen/schemas/retention_policy_assignment.py @@ -0,0 +1,113 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.retention_policy_mini import RetentionPolicyMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class RetentionPolicyAssignmentTypeField(str, Enum): + RETENTION_POLICY_ASSIGNMENT = 'retention_policy_assignment' + + +class RetentionPolicyAssignmentAssignedToTypeField(str, Enum): + FOLDER = 'folder' + ENTERPRISE = 'enterprise' + METADATA_TEMPLATE = 'metadata_template' + + +class RetentionPolicyAssignmentAssignedToField(BaseObject): + _discriminator = 'type', {'folder', 'enterprise', 'metadata_template'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[RetentionPolicyAssignmentAssignedToTypeField] = None, + **kwargs + ): + """ + :param id: The ID of the folder, enterprise, or metadata template + the policy is assigned to. + Set to null or omit when type is set to enterprise., defaults to None + :type id: Optional[str], optional + :param type: The type of resource the policy is assigned to., defaults to None + :type type: Optional[RetentionPolicyAssignmentAssignedToTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class RetentionPolicyAssignmentFilterFieldsField(BaseObject): + def __init__( + self, *, field: Optional[str] = None, value: Optional[str] = None, **kwargs + ): + """ + :param field: The metadata attribute key id., defaults to None + :type field: Optional[str], optional + :param value: The metadata attribute field id. For value, only + enum and multiselect types are supported., defaults to None + :type value: Optional[str], optional + """ + super().__init__(**kwargs) + self.field = field + self.value = value + + +class RetentionPolicyAssignment(BaseObject): + _discriminator = 'type', {'retention_policy_assignment'} + + def __init__( + self, + id: str, + *, + type: RetentionPolicyAssignmentTypeField = RetentionPolicyAssignmentTypeField.RETENTION_POLICY_ASSIGNMENT, + retention_policy: Optional[RetentionPolicyMini] = None, + assigned_to: Optional[RetentionPolicyAssignmentAssignedToField] = None, + filter_fields: Optional[ + List[RetentionPolicyAssignmentFilterFieldsField] + ] = None, + assigned_by: Optional[UserMini] = None, + assigned_at: Optional[DateTime] = None, + start_date_field: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for a retention policy assignment. + :type id: str + :param type: The value will always be `retention_policy_assignment`., defaults to RetentionPolicyAssignmentTypeField.RETENTION_POLICY_ASSIGNMENT + :type type: RetentionPolicyAssignmentTypeField, optional + :param assigned_to: The `type` and `id` of the content that is under + retention. The `type` can either be `folder` + `enterprise`, or `metadata_template`., defaults to None + :type assigned_to: Optional[RetentionPolicyAssignmentAssignedToField], optional + :param filter_fields: An array of field objects. Values are only returned if the `assigned_to` + type is `metadata_template`. Otherwise, the array is blank., defaults to None + :type filter_fields: Optional[List[RetentionPolicyAssignmentFilterFieldsField]], optional + :param assigned_at: When the retention policy assignment object was + created., defaults to None + :type assigned_at: Optional[DateTime], optional + :param start_date_field: The date the retention policy assignment begins. + If the `assigned_to` type is `metadata_template`, + this field can be a date field's metadata attribute key id., defaults to None + :type start_date_field: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.retention_policy = retention_policy + self.assigned_to = assigned_to + self.filter_fields = filter_fields + self.assigned_by = assigned_by + self.assigned_at = assigned_at + self.start_date_field = start_date_field diff --git a/box_sdk_gen/schemas/retention_policy_assignments.py b/box_sdk_gen/schemas/retention_policy_assignments.py new file mode 100644 index 000000000..5429d0cfc --- /dev/null +++ b/box_sdk_gen/schemas/retention_policy_assignments.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.retention_policy_assignment import RetentionPolicyAssignment + +from box_sdk_gen.box.errors import BoxSDKError + + +class RetentionPolicyAssignments(BaseObject): + def __init__( + self, + *, + entries: Optional[List[RetentionPolicyAssignment]] = None, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: A list of retention policy assignments., defaults to None + :type entries: Optional[List[RetentionPolicyAssignment]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/retention_policy_base.py b/box_sdk_gen/schemas/retention_policy_base.py new file mode 100644 index 000000000..550261c73 --- /dev/null +++ b/box_sdk_gen/schemas/retention_policy_base.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class RetentionPolicyBaseTypeField(str, Enum): + RETENTION_POLICY = 'retention_policy' + + +class RetentionPolicyBase(BaseObject): + _discriminator = 'type', {'retention_policy'} + + def __init__( + self, + id: str, + *, + type: RetentionPolicyBaseTypeField = RetentionPolicyBaseTypeField.RETENTION_POLICY, + **kwargs + ): + """ + :param id: The unique identifier that represents a retention policy. + :type id: str + :param type: The value will always be `retention_policy`., defaults to RetentionPolicyBaseTypeField.RETENTION_POLICY + :type type: RetentionPolicyBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/retention_policy_mini.py b/box_sdk_gen/schemas/retention_policy_mini.py new file mode 100644 index 000000000..ff8ad0b20 --- /dev/null +++ b/box_sdk_gen/schemas/retention_policy_mini.py @@ -0,0 +1,54 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.retention_policy_base import RetentionPolicyBaseTypeField + +from box_sdk_gen.schemas.retention_policy_base import RetentionPolicyBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class RetentionPolicyMiniDispositionActionField(str, Enum): + PERMANENTLY_DELETE = 'permanently_delete' + REMOVE_RETENTION = 'remove_retention' + + +class RetentionPolicyMini(RetentionPolicyBase): + def __init__( + self, + id: str, + *, + policy_name: Optional[str] = None, + retention_length: Optional[str] = None, + disposition_action: Optional[RetentionPolicyMiniDispositionActionField] = None, + type: RetentionPolicyBaseTypeField = RetentionPolicyBaseTypeField.RETENTION_POLICY, + **kwargs + ): + """ + :param id: The unique identifier that represents a retention policy. + :type id: str + :param policy_name: The name given to the retention policy., defaults to None + :type policy_name: Optional[str], optional + :param retention_length: The length of the retention policy. This value + specifies the duration in days that the retention + policy will be active for after being assigned to + content. If the policy has a `policy_type` of + `indefinite`, the `retention_length` will also be + `indefinite`., defaults to None + :type retention_length: Optional[str], optional + :param disposition_action: The disposition action of the retention policy. + This action can be `permanently_delete`, which + will cause the content retained by the policy + to be permanently deleted, or `remove_retention`, + which will lift the retention policy from the content, + allowing it to be deleted by users, + once the retention policy has expired., defaults to None + :type disposition_action: Optional[RetentionPolicyMiniDispositionActionField], optional + :param type: The value will always be `retention_policy`., defaults to RetentionPolicyBaseTypeField.RETENTION_POLICY + :type type: RetentionPolicyBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.policy_name = policy_name + self.retention_length = retention_length + self.disposition_action = disposition_action diff --git a/box_sdk_gen/schemas/role_variable.py b/box_sdk_gen/schemas/role_variable.py new file mode 100644 index 000000000..9e35facdf --- /dev/null +++ b/box_sdk_gen/schemas/role_variable.py @@ -0,0 +1,47 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class RoleVariableTypeField(str, Enum): + VARIABLE = 'variable' + + +class RoleVariableVariableTypeField(str, Enum): + COLLABORATOR_ROLE = 'collaborator_role' + + +class RoleVariableVariableValueField(str, Enum): + EDITOR = 'editor' + VIEWER = 'viewer' + PREVIEWER = 'previewer' + UPLOADER = 'uploader' + PREVIEWER_UPLOADER = 'previewer uploader' + VIEWER_UPLOADER = 'viewer uploader' + CO_OWNER = 'co-owner' + + +class RoleVariable(BaseObject): + _discriminator = 'type', {'variable'} + + def __init__( + self, + variable_value: RoleVariableVariableValueField, + *, + type: RoleVariableTypeField = RoleVariableTypeField.VARIABLE, + variable_type: RoleVariableVariableTypeField = RoleVariableVariableTypeField.COLLABORATOR_ROLE, + **kwargs + ): + """ + :param type: Role object type., defaults to RoleVariableTypeField.VARIABLE + :type type: RoleVariableTypeField, optional + :param variable_type: The variable type used + by the object., defaults to RoleVariableVariableTypeField.COLLABORATOR_ROLE + :type variable_type: RoleVariableVariableTypeField, optional + """ + super().__init__(**kwargs) + self.variable_value = variable_value + self.type = type + self.variable_type = variable_type diff --git a/box_sdk_gen/schemas/search_result_item.py b/box_sdk_gen/schemas/search_result_item.py new file mode 100644 index 000000000..62f136e3c --- /dev/null +++ b/box_sdk_gen/schemas/search_result_item.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.box.errors import BoxSDKError + +SearchResultItem = Union[FileFull, FolderFull, WebLink] diff --git a/box_sdk_gen/schemas/search_result_with_shared_link.py b/box_sdk_gen/schemas/search_result_with_shared_link.py new file mode 100644 index 000000000..f3783d4d0 --- /dev/null +++ b/box_sdk_gen/schemas/search_result_with_shared_link.py @@ -0,0 +1,39 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.search_result_with_shared_link_item import ( + SearchResultWithSharedLinkItem, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class SearchResultWithSharedLink(BaseObject): + def __init__( + self, + *, + accessible_via_shared_link: Optional[str] = None, + item: Optional[SearchResultWithSharedLinkItem] = None, + type: Optional[str] = None, + **kwargs + ): + """ + :param accessible_via_shared_link: The optional shared link through which the user has access to this + item. This value is only returned for items for which the user has + recently accessed the file through a shared link. For all other + items this value will return `null`., defaults to None + :type accessible_via_shared_link: Optional[str], optional + :param type: The result type. The value is always `search_result`., defaults to None + :type type: Optional[str], optional + """ + super().__init__(**kwargs) + self.accessible_via_shared_link = accessible_via_shared_link + self.item = item + self.type = type diff --git a/box_sdk_gen/schemas/search_result_with_shared_link_item.py b/box_sdk_gen/schemas/search_result_with_shared_link_item.py new file mode 100644 index 000000000..8938f59e6 --- /dev/null +++ b/box_sdk_gen/schemas/search_result_with_shared_link_item.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.box.errors import BoxSDKError + +SearchResultWithSharedLinkItem = Union[FileFull, FolderFull, WebLink] diff --git a/box_sdk_gen/schemas/search_results.py b/box_sdk_gen/schemas/search_results.py new file mode 100644 index 000000000..b4ea14cd1 --- /dev/null +++ b/box_sdk_gen/schemas/search_results.py @@ -0,0 +1,59 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_full import FileFull + +from box_sdk_gen.schemas.folder_full import FolderFull + +from box_sdk_gen.schemas.web_link import WebLink + +from box_sdk_gen.schemas.search_result_item import SearchResultItem + +from box_sdk_gen.box.errors import BoxSDKError + + +class SearchResultsTypeField(str, Enum): + SEARCH_RESULTS_ITEMS = 'search_results_items' + + +class SearchResults(BaseObject): + _discriminator = 'type', {'search_results_items'} + + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + type: SearchResultsTypeField = SearchResultsTypeField.SEARCH_RESULTS_ITEMS, + entries: Optional[List[SearchResultItem]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the search results. + The total number of entries in the collection may be less than + `total_count`., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for this search. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter used., defaults to None + :type offset: Optional[int], optional + :param type: Specifies the response as search result items without shared links., defaults to SearchResultsTypeField.SEARCH_RESULTS_ITEMS + :type type: SearchResultsTypeField, optional + :param entries: The search results for the query provided., defaults to None + :type entries: Optional[List[SearchResultItem]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.type = type + self.entries = entries diff --git a/box_sdk_gen/schemas/search_results_response.py b/box_sdk_gen/schemas/search_results_response.py new file mode 100644 index 000000000..252c70f50 --- /dev/null +++ b/box_sdk_gen/schemas/search_results_response.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.search_results import SearchResults + +from box_sdk_gen.schemas.search_results_with_shared_links import ( + SearchResultsWithSharedLinks, +) + +from box_sdk_gen.box.errors import BoxSDKError + +SearchResultsResponse = Union[SearchResults, SearchResultsWithSharedLinks] diff --git a/box_sdk_gen/schemas/search_results_with_shared_links.py b/box_sdk_gen/schemas/search_results_with_shared_links.py new file mode 100644 index 000000000..cdf023cba --- /dev/null +++ b/box_sdk_gen/schemas/search_results_with_shared_links.py @@ -0,0 +1,57 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.search_result_with_shared_link import ( + SearchResultWithSharedLink, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class SearchResultsWithSharedLinksTypeField(str, Enum): + SEARCH_RESULTS_WITH_SHARED_LINKS = 'search_results_with_shared_links' + + +class SearchResultsWithSharedLinks(BaseObject): + _discriminator = 'type', {'search_results_with_shared_links'} + + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + type: SearchResultsWithSharedLinksTypeField = SearchResultsWithSharedLinksTypeField.SEARCH_RESULTS_WITH_SHARED_LINKS, + entries: Optional[List[SearchResultWithSharedLink]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the search results. + The total number of entries in the collection may be less than + `total_count`., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for this search. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter used., defaults to None + :type offset: Optional[int], optional + :param type: Specifies the response as search result items with shared links., defaults to SearchResultsWithSharedLinksTypeField.SEARCH_RESULTS_WITH_SHARED_LINKS + :type type: SearchResultsWithSharedLinksTypeField, optional + :param entries: The search results for the query provided, including the + additional information about any shared links through + which the item has been shared with the user., defaults to None + :type entries: Optional[List[SearchResultWithSharedLink]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.type = type + self.entries = entries diff --git a/box_sdk_gen/schemas/session_termination_message.py b/box_sdk_gen/schemas/session_termination_message.py new file mode 100644 index 000000000..2afe2acdb --- /dev/null +++ b/box_sdk_gen/schemas/session_termination_message.py @@ -0,0 +1,15 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class SessionTerminationMessage(BaseObject): + def __init__(self, *, message: Optional[str] = None, **kwargs): + """ + :param message: The unique identifier for the termination job status., defaults to None + :type message: Optional[str], optional + """ + super().__init__(**kwargs) + self.message = message diff --git a/box_sdk_gen/schemas/shield_information_barrier.py b/box_sdk_gen/schemas/shield_information_barrier.py new file mode 100644 index 000000000..efe6b2212 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier.py @@ -0,0 +1,77 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.enterprise_base import EnterpriseBase + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class ShieldInformationBarrierTypeField(str, Enum): + SHIELD_INFORMATION_BARRIER = 'shield_information_barrier' + + +class ShieldInformationBarrierStatusField(str, Enum): + DRAFT = 'draft' + PENDING = 'pending' + DISABLED = 'disabled' + ENABLED = 'enabled' + INVALID = 'invalid' + + +class ShieldInformationBarrier(BaseObject): + _discriminator = 'type', {'shield_information_barrier'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierTypeField] = None, + enterprise: Optional[EnterpriseBase] = None, + status: Optional[ShieldInformationBarrierStatusField] = None, + created_at: Optional[DateTime] = None, + created_by: Optional[UserBase] = None, + updated_at: Optional[DateTime] = None, + updated_by: Optional[UserBase] = None, + enabled_at: Optional[DateTime] = None, + enabled_by: Optional[UserBase] = None, + **kwargs + ): + """ + :param id: The unique identifier for the shield information barrier., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier., defaults to None + :type type: Optional[ShieldInformationBarrierTypeField], optional + :param enterprise: The `type` and `id` of enterprise this barrier is under., defaults to None + :type enterprise: Optional[EnterpriseBase], optional + :param status: Status of the shield information barrier., defaults to None + :type status: Optional[ShieldInformationBarrierStatusField], optional + :param created_at: ISO date time string when this + shield information barrier object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param created_by: The user who created this shield information barrier., defaults to None + :type created_by: Optional[UserBase], optional + :param updated_at: ISO date time string when this shield information barrier was updated., defaults to None + :type updated_at: Optional[DateTime], optional + :param updated_by: The user that updated this shield information barrier., defaults to None + :type updated_by: Optional[UserBase], optional + :param enabled_at: ISO date time string when this shield information barrier was enabled., defaults to None + :type enabled_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.enterprise = enterprise + self.status = status + self.created_at = created_at + self.created_by = created_by + self.updated_at = updated_at + self.updated_by = updated_by + self.enabled_at = enabled_at + self.enabled_by = enabled_by diff --git a/box_sdk_gen/schemas/shield_information_barrier_base.py b/box_sdk_gen/schemas/shield_information_barrier_base.py new file mode 100644 index 000000000..2181be859 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_base.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierBaseTypeField(str, Enum): + SHIELD_INFORMATION_BARRIER = 'shield_information_barrier' + + +class ShieldInformationBarrierBase(BaseObject): + _discriminator = 'type', {'shield_information_barrier'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierBaseTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for the shield information barrier., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier., defaults to None + :type type: Optional[ShieldInformationBarrierBaseTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/shield_information_barrier_reference.py b/box_sdk_gen/schemas/shield_information_barrier_reference.py new file mode 100644 index 000000000..c2f4debff --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_reference.py @@ -0,0 +1,20 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierReference(BaseObject): + def __init__( + self, + *, + shield_information_barrier: Optional[ShieldInformationBarrierBase] = None, + **kwargs + ): + super().__init__(**kwargs) + self.shield_information_barrier = shield_information_barrier diff --git a/box_sdk_gen/schemas/shield_information_barrier_report.py b/box_sdk_gen/schemas/shield_information_barrier_report.py new file mode 100644 index 000000000..4701838e4 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_report.py @@ -0,0 +1,69 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.shield_information_barrier_report_base import ( + ShieldInformationBarrierReportBaseTypeField, +) + +from box_sdk_gen.schemas.shield_information_barrier_report_base import ( + ShieldInformationBarrierReportBase, +) + +from box_sdk_gen.schemas.shield_information_barrier_reference import ( + ShieldInformationBarrierReference, +) + +from box_sdk_gen.schemas.shield_information_barrier_report_details import ( + ShieldInformationBarrierReportDetails, +) + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class ShieldInformationBarrierReportStatusField(str, Enum): + PENDING = 'pending' + ERROR = 'error' + DONE = 'done' + CANCELLED = 'cancelled' + + +class ShieldInformationBarrierReport(ShieldInformationBarrierReportBase): + def __init__( + self, + *, + shield_information_barrier: Optional[ShieldInformationBarrierReference] = None, + status: Optional[ShieldInformationBarrierReportStatusField] = None, + details: Optional[ShieldInformationBarrierReportDetails] = None, + created_at: Optional[DateTime] = None, + created_by: Optional[UserBase] = None, + updated_at: Optional[DateTime] = None, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierReportBaseTypeField] = None, + **kwargs + ): + """ + :param status: Status of the shield information report., defaults to None + :type status: Optional[ShieldInformationBarrierReportStatusField], optional + :param created_at: ISO date time string when this + shield information barrier report object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param updated_at: ISO date time string when this + shield information barrier report was updated., defaults to None + :type updated_at: Optional[DateTime], optional + :param id: The unique identifier for the shield information barrier report., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier report., defaults to None + :type type: Optional[ShieldInformationBarrierReportBaseTypeField], optional + """ + super().__init__(id=id, type=type, **kwargs) + self.shield_information_barrier = shield_information_barrier + self.status = status + self.details = details + self.created_at = created_at + self.created_by = created_by + self.updated_at = updated_at diff --git a/box_sdk_gen/schemas/shield_information_barrier_report_base.py b/box_sdk_gen/schemas/shield_information_barrier_report_base.py new file mode 100644 index 000000000..d23f05aad --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_report_base.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierReportBaseTypeField(str, Enum): + SHIELD_INFORMATION_BARRIER_REPORT = 'shield_information_barrier_report' + + +class ShieldInformationBarrierReportBase(BaseObject): + _discriminator = 'type', {'shield_information_barrier_report'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierReportBaseTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for the shield information barrier report., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier report., defaults to None + :type type: Optional[ShieldInformationBarrierReportBaseTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/shield_information_barrier_report_details.py b/box_sdk_gen/schemas/shield_information_barrier_report_details.py new file mode 100644 index 000000000..e26e8e7cf --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_report_details.py @@ -0,0 +1,26 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierReportDetailsDetailsField(BaseObject): + def __init__(self, *, folder_id: Optional[str] = None, **kwargs): + """ + :param folder_id: Folder ID for locating this report., defaults to None + :type folder_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.folder_id = folder_id + + +class ShieldInformationBarrierReportDetails(BaseObject): + def __init__( + self, + *, + details: Optional[ShieldInformationBarrierReportDetailsDetailsField] = None, + **kwargs + ): + super().__init__(**kwargs) + self.details = details diff --git a/box_sdk_gen/schemas/shield_information_barrier_reports.py b/box_sdk_gen/schemas/shield_information_barrier_reports.py new file mode 100644 index 000000000..76c81ab9a --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_reports.py @@ -0,0 +1,37 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_report import ( + ShieldInformationBarrierReport, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierReports(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + entries: Optional[List[ShieldInformationBarrierReport]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param entries: A list of shield information + barrier reports., defaults to None + :type entries: Optional[List[ShieldInformationBarrierReport]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment.py b/box_sdk_gen/schemas/shield_information_barrier_segment.py new file mode 100644 index 000000000..9bd253a10 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment.py @@ -0,0 +1,64 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class ShieldInformationBarrierSegmentTypeField(str, Enum): + SHIELD_INFORMATION_BARRIER_SEGMENT = 'shield_information_barrier_segment' + + +class ShieldInformationBarrierSegment(BaseObject): + _discriminator = 'type', {'shield_information_barrier_segment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierSegmentTypeField] = None, + shield_information_barrier: Optional[ShieldInformationBarrierBase] = None, + name: Optional[str] = None, + description: Optional[str] = None, + created_at: Optional[DateTime] = None, + created_by: Optional[UserBase] = None, + updated_at: Optional[DateTime] = None, + updated_by: Optional[UserBase] = None, + **kwargs + ): + """ + :param id: The unique identifier for the shield information barrier segment., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier segment., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentTypeField], optional + :param name: Name of the shield information barrier segment., defaults to None + :type name: Optional[str], optional + :param description: Description of the shield information barrier segment., defaults to None + :type description: Optional[str], optional + :param created_at: ISO date time string when this shield information + barrier object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param updated_at: ISO date time string when this + shield information barrier segment was updated., defaults to None + :type updated_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.shield_information_barrier = shield_information_barrier + self.name = name + self.description = description + self.created_at = created_at + self.created_by = created_by + self.updated_at = updated_at + self.updated_by = updated_by diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_member.py b/box_sdk_gen/schemas/shield_information_barrier_segment_member.py new file mode 100644 index 000000000..0a037bc8c --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_member.py @@ -0,0 +1,101 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_segment_member_base import ( + ShieldInformationBarrierSegmentMemberBaseTypeField, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_member_base import ( + ShieldInformationBarrierSegmentMemberBase, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_member_mini import ( + ShieldInformationBarrierSegmentMemberMini, +) + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class ShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentTypeField( + str, Enum +): + SHIELD_INFORMATION_BARRIER_SEGMENT = 'shield_information_barrier_segment' + + +class ShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentField( + BaseObject +): + _discriminator = 'type', {'shield_information_barrier_segment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ + ShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentTypeField + ] = None, + **kwargs + ): + """ + :param id: The ID reference of the requesting + shield information barrier segment., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier segment., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class ShieldInformationBarrierSegmentMember(ShieldInformationBarrierSegmentMemberMini): + def __init__( + self, + *, + shield_information_barrier: Optional[ShieldInformationBarrierBase] = None, + shield_information_barrier_segment: Optional[ + ShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentField + ] = None, + created_at: Optional[DateTime] = None, + created_by: Optional[UserBase] = None, + updated_at: Optional[DateTime] = None, + updated_by: Optional[UserBase] = None, + user: Optional[UserBase] = None, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierSegmentMemberBaseTypeField] = None, + **kwargs + ): + """ + :param shield_information_barrier_segment: The `type` and `id` of the requested + shield information barrier segment., defaults to None + :type shield_information_barrier_segment: Optional[ShieldInformationBarrierSegmentMemberShieldInformationBarrierSegmentField], optional + :param created_at: ISO date time string when this shield + information barrier object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param updated_at: ISO date time string when this + shield information barrier segment Member was updated., defaults to None + :type updated_at: Optional[DateTime], optional + :param id: The unique identifier for the + shield information barrier segment member., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier segment member., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentMemberBaseTypeField], optional + """ + super().__init__(user=user, id=id, type=type, **kwargs) + self.shield_information_barrier = shield_information_barrier + self.shield_information_barrier_segment = shield_information_barrier_segment + self.created_at = created_at + self.created_by = created_by + self.updated_at = updated_at + self.updated_by = updated_by diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_member_base.py b/box_sdk_gen/schemas/shield_information_barrier_segment_member_base.py new file mode 100644 index 000000000..28c9158a2 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_member_base.py @@ -0,0 +1,35 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierSegmentMemberBaseTypeField(str, Enum): + SHIELD_INFORMATION_BARRIER_SEGMENT_MEMBER = ( + 'shield_information_barrier_segment_member' + ) + + +class ShieldInformationBarrierSegmentMemberBase(BaseObject): + _discriminator = 'type', {'shield_information_barrier_segment_member'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierSegmentMemberBaseTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for the + shield information barrier segment member., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier segment member., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentMemberBaseTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_member_mini.py b/box_sdk_gen/schemas/shield_information_barrier_segment_member_mini.py new file mode 100644 index 000000000..ef416af8a --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_member_mini.py @@ -0,0 +1,35 @@ +from typing import Optional + +from box_sdk_gen.schemas.shield_information_barrier_segment_member_base import ( + ShieldInformationBarrierSegmentMemberBaseTypeField, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_member_base import ( + ShieldInformationBarrierSegmentMemberBase, +) + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierSegmentMemberMini( + ShieldInformationBarrierSegmentMemberBase +): + def __init__( + self, + *, + user: Optional[UserBase] = None, + id: Optional[str] = None, + type: Optional[ShieldInformationBarrierSegmentMemberBaseTypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for the + shield information barrier segment member., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier segment member., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentMemberBaseTypeField], optional + """ + super().__init__(id=id, type=type, **kwargs) + self.user = user diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_members.py b/box_sdk_gen/schemas/shield_information_barrier_segment_members.py new file mode 100644 index 000000000..86cd4dca0 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_members.py @@ -0,0 +1,37 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_segment_member import ( + ShieldInformationBarrierSegmentMember, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierSegmentMembers(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + entries: Optional[List[ShieldInformationBarrierSegmentMember]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param entries: A list of shield information + barrier segment members., defaults to None + :type entries: Optional[List[ShieldInformationBarrierSegmentMember]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_restriction.py b/box_sdk_gen/schemas/shield_information_barrier_segment_restriction.py new file mode 100644 index 000000000..2715bcb33 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_restriction.py @@ -0,0 +1,83 @@ +from typing import Optional + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_base import ( + ShieldInformationBarrierSegmentRestrictionBaseTypeField, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_base import ( + ShieldInformationBarrierSegmentRestrictionBase, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_mini import ( + ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentField, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_mini import ( + ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentField, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_mini import ( + ShieldInformationBarrierSegmentRestrictionMini, +) + +from box_sdk_gen.schemas.shield_information_barrier_base import ( + ShieldInformationBarrierBase, +) + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class ShieldInformationBarrierSegmentRestriction( + ShieldInformationBarrierSegmentRestrictionMini +): + def __init__( + self, + shield_information_barrier_segment: ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentField, + restricted_segment: ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentField, + *, + shield_information_barrier: Optional[ShieldInformationBarrierBase] = None, + created_at: Optional[DateTime] = None, + created_by: Optional[UserBase] = None, + updated_at: Optional[DateTime] = None, + updated_by: Optional[UserBase] = None, + type: Optional[ShieldInformationBarrierSegmentRestrictionBaseTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param shield_information_barrier_segment: The `type` and `id` of the + requested shield information barrier segment. + :type shield_information_barrier_segment: ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentField + :param restricted_segment: The `type` and `id` of the + restricted shield information barrier segment. + :type restricted_segment: ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentField + :param created_at: ISO date time string when this + shield information barrier + Segment Restriction object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param updated_at: ISO date time string when this + shield information barrier segment + Restriction was updated., defaults to None + :type updated_at: Optional[DateTime], optional + :param type: Shield information barrier segment restriction., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentRestrictionBaseTypeField], optional + :param id: The unique identifier for the + shield information barrier segment restriction., defaults to None + :type id: Optional[str], optional + """ + super().__init__( + shield_information_barrier_segment=shield_information_barrier_segment, + restricted_segment=restricted_segment, + type=type, + id=id, + **kwargs + ) + self.shield_information_barrier = shield_information_barrier + self.created_at = created_at + self.created_by = created_by + self.updated_at = updated_at + self.updated_by = updated_by diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_restriction_base.py b/box_sdk_gen/schemas/shield_information_barrier_segment_restriction_base.py new file mode 100644 index 000000000..49dbccf56 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_restriction_base.py @@ -0,0 +1,35 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierSegmentRestrictionBaseTypeField(str, Enum): + SHIELD_INFORMATION_BARRIER_SEGMENT_RESTRICTION = ( + 'shield_information_barrier_segment_restriction' + ) + + +class ShieldInformationBarrierSegmentRestrictionBase(BaseObject): + _discriminator = 'type', {'shield_information_barrier_segment_restriction'} + + def __init__( + self, + *, + type: Optional[ShieldInformationBarrierSegmentRestrictionBaseTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: Shield information barrier segment restriction., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentRestrictionBaseTypeField], optional + :param id: The unique identifier for the + shield information barrier segment restriction., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_restriction_mini.py b/box_sdk_gen/schemas/shield_information_barrier_segment_restriction_mini.py new file mode 100644 index 000000000..a84864fe2 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_restriction_mini.py @@ -0,0 +1,107 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_base import ( + ShieldInformationBarrierSegmentRestrictionBaseTypeField, +) + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction_base import ( + ShieldInformationBarrierSegmentRestrictionBase, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentTypeField( + str, Enum +): + SHIELD_INFORMATION_BARRIER_SEGMENT = 'shield_information_barrier_segment' + + +class ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentField( + BaseObject +): + _discriminator = 'type', {'shield_information_barrier_segment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ + ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentTypeField + ] = None, + **kwargs + ): + """ + :param id: The ID reference of the + requesting shield information barrier segment., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information barrier segment., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentTypeField( + str, Enum +): + SHIELD_INFORMATION_BARRIER_SEGMENT = 'shield_information_barrier_segment' + + +class ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentField(BaseObject): + _discriminator = 'type', {'shield_information_barrier_segment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ + ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentTypeField + ] = None, + **kwargs + ): + """ + :param id: The ID reference of the + restricted shield information barrier segment., defaults to None + :type id: Optional[str], optional + :param type: The type of the shield information segment., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class ShieldInformationBarrierSegmentRestrictionMini( + ShieldInformationBarrierSegmentRestrictionBase +): + def __init__( + self, + shield_information_barrier_segment: ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentField, + restricted_segment: ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentField, + *, + type: Optional[ShieldInformationBarrierSegmentRestrictionBaseTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param shield_information_barrier_segment: The `type` and `id` of the + requested shield information barrier segment. + :type shield_information_barrier_segment: ShieldInformationBarrierSegmentRestrictionMiniShieldInformationBarrierSegmentField + :param restricted_segment: The `type` and `id` of the + restricted shield information barrier segment. + :type restricted_segment: ShieldInformationBarrierSegmentRestrictionMiniRestrictedSegmentField + :param type: Shield information barrier segment restriction., defaults to None + :type type: Optional[ShieldInformationBarrierSegmentRestrictionBaseTypeField], optional + :param id: The unique identifier for the + shield information barrier segment restriction., defaults to None + :type id: Optional[str], optional + """ + super().__init__(type=type, id=id, **kwargs) + self.shield_information_barrier_segment = shield_information_barrier_segment + self.restricted_segment = restricted_segment diff --git a/box_sdk_gen/schemas/shield_information_barrier_segment_restrictions.py b/box_sdk_gen/schemas/shield_information_barrier_segment_restrictions.py new file mode 100644 index 000000000..7f8e625c7 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segment_restrictions.py @@ -0,0 +1,37 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_segment_restriction import ( + ShieldInformationBarrierSegmentRestriction, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierSegmentRestrictions(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + entries: Optional[List[ShieldInformationBarrierSegmentRestriction]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param entries: A list of shield information barrier + segment restriction objects., defaults to None + :type entries: Optional[List[ShieldInformationBarrierSegmentRestriction]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/shield_information_barrier_segments.py b/box_sdk_gen/schemas/shield_information_barrier_segments.py new file mode 100644 index 000000000..8a4ab20d6 --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barrier_segments.py @@ -0,0 +1,37 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier_segment import ( + ShieldInformationBarrierSegment, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarrierSegments(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + entries: Optional[List[ShieldInformationBarrierSegment]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param entries: A list of shield information barrier + segments., defaults to None + :type entries: Optional[List[ShieldInformationBarrierSegment]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/shield_information_barriers.py b/box_sdk_gen/schemas/shield_information_barriers.py new file mode 100644 index 000000000..475d83d5b --- /dev/null +++ b/box_sdk_gen/schemas/shield_information_barriers.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.shield_information_barrier import ShieldInformationBarrier + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldInformationBarriers(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + entries: Optional[List[ShieldInformationBarrier]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param entries: A list of shield information barrier objects., defaults to None + :type entries: Optional[List[ShieldInformationBarrier]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/sign_request.py b/box_sdk_gen/schemas/sign_request.py new file mode 100644 index 000000000..16d904d1c --- /dev/null +++ b/box_sdk_gen/schemas/sign_request.py @@ -0,0 +1,187 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.sign_request_prefill_tag import SignRequestPrefillTag + +from box_sdk_gen.schemas.sign_request_base import SignRequestBase + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.sign_request_signer import SignRequestSigner + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class SignRequestTypeField(str, Enum): + SIGN_REQUEST = 'sign-request' + + +class SignRequestStatusField(str, Enum): + CONVERTING = 'converting' + CREATED = 'created' + SENT = 'sent' + VIEWED = 'viewed' + SIGNED = 'signed' + CANCELLED = 'cancelled' + DECLINED = 'declined' + ERROR_CONVERTING = 'error_converting' + ERROR_SENDING = 'error_sending' + EXPIRED = 'expired' + FINALIZING = 'finalizing' + ERROR_FINALIZING = 'error_finalizing' + + +class SignRequestSignFilesField(BaseObject): + def __init__( + self, + *, + files: Optional[List[FileMini]] = None, + is_ready_for_download: Optional[bool] = None, + **kwargs + ): + """ + :param is_ready_for_download: Indicates whether the `sign_files` documents are processing + and the PDFs may be out of date. A change to any document + requires processing on all `sign_files`. We + recommended waiting until processing is finished + (and this value is true) before downloading the PDFs., defaults to None + :type is_ready_for_download: Optional[bool], optional + """ + super().__init__(**kwargs) + self.files = files + self.is_ready_for_download = is_ready_for_download + + +class SignRequest(SignRequestBase): + def __init__( + self, + *, + type: Optional[SignRequestTypeField] = None, + source_files: Optional[List[FileBase]] = None, + signers: Optional[List[SignRequestSigner]] = None, + signature_color: Optional[str] = None, + id: Optional[str] = None, + prepare_url: Optional[str] = None, + signing_log: Optional[FileMini] = None, + status: Optional[SignRequestStatusField] = None, + sign_files: Optional[SignRequestSignFilesField] = None, + auto_expire_at: Optional[DateTime] = None, + parent_folder: Optional[FolderMini] = None, + collaborator_level: Optional[str] = None, + sender_email: Optional[str] = None, + sender_id: Optional[int] = None, + is_document_preparation_needed: Optional[bool] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + are_text_signatures_enabled: Optional[bool] = None, + email_subject: Optional[str] = None, + email_message: Optional[str] = None, + are_reminders_enabled: Optional[bool] = None, + name: Optional[str] = None, + prefill_tags: Optional[List[SignRequestPrefillTag]] = None, + days_valid: Optional[int] = None, + external_id: Optional[str] = None, + template_id: Optional[str] = None, + external_system_name: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `sign-request`., defaults to None + :type type: Optional[SignRequestTypeField], optional + :param source_files: List of files to create a signing document from. This is currently limited to ten files. Only the ID and type fields are required for each file., defaults to None + :type source_files: Optional[List[FileBase]], optional + :param signers: Array of signers for the signature request., defaults to None + :type signers: Optional[List[SignRequestSigner]], optional + :param signature_color: Force a specific color for the signature (blue, black, or red)., defaults to None + :type signature_color: Optional[str], optional + :param id: Box Sign request ID., defaults to None + :type id: Optional[str], optional + :param prepare_url: This URL is returned if `is_document_preparation_needed` is + set to `true` in the request. The parameter is used to prepare + the signature request + using the UI. The signature request is not + sent until the preparation + phase is complete., defaults to None + :type prepare_url: Optional[str], optional + :param status: Describes the status of the signature request., defaults to None + :type status: Optional[SignRequestStatusField], optional + :param sign_files: List of files that will be signed, which are copies of the original + source files. A new version of these files are created as signers sign + and can be downloaded at any point in the signing process., defaults to None + :type sign_files: Optional[SignRequestSignFilesField], optional + :param auto_expire_at: Uses `days_valid` to calculate the date and time, in GMT, the sign request will expire if unsigned., defaults to None + :type auto_expire_at: Optional[DateTime], optional + :param collaborator_level: The collaborator level of the user to the sign request. Values can include "owner", "editor", and "viewer"., defaults to None + :type collaborator_level: Optional[str], optional + :param sender_email: The email address of the sender of the sign request., defaults to None + :type sender_email: Optional[str], optional + :param sender_id: The user ID of the sender of the sign request., defaults to None + :type sender_id: Optional[int], optional + :param is_document_preparation_needed: Indicates if the sender should receive a `prepare_url` in the response to complete document preparation using the UI., defaults to None + :type is_document_preparation_needed: Optional[bool], optional + :param redirect_url: When specified, the signature request will be redirected to this url when a document is signed., defaults to None + :type redirect_url: Optional[str], optional + :param declined_redirect_url: The uri that a signer will be redirected to after declining to sign a document., defaults to None + :type declined_redirect_url: Optional[str], optional + :param are_text_signatures_enabled: Disables the usage of signatures generated by typing (text)., defaults to None + :type are_text_signatures_enabled: Optional[bool], optional + :param email_subject: Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used., defaults to None + :type email_subject: Optional[str], optional + :param email_message: Message to include in sign request email. The field is cleaned through sanitization of specific characters. However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email. The message may contain the following html tags including `a`, `abbr`, `acronym`, `b`, `blockquote`, `code`, `em`, `i`, `ul`, `li`, `ol`, and `strong`. Be aware that when the text to html ratio is too high, the email may end up in spam filters. Custom styles on these tags are not allowed. If this field is not passed, a default message will be used., defaults to None + :type email_message: Optional[str], optional + :param are_reminders_enabled: Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers., defaults to None + :type are_reminders_enabled: Optional[bool], optional + :param name: Name of the signature request., defaults to None + :type name: Optional[str], optional + :param prefill_tags: When a document contains sign-related tags in the content, you can prefill them using this `prefill_tags` by referencing the 'id' of the tag as the `external_id` field of the prefill tag., defaults to None + :type prefill_tags: Optional[List[SignRequestPrefillTag]], optional + :param days_valid: Set the number of days after which the created signature request will automatically expire if not completed. By default, we do not apply any expiration date on signature requests, and the signature request does not expire., defaults to None + :type days_valid: Optional[int], optional + :param external_id: This can be used to reference an ID in an external system that the sign request is related to., defaults to None + :type external_id: Optional[str], optional + :param template_id: When a signature request is created from a template this field will indicate the id of that template., defaults to None + :type template_id: Optional[str], optional + :param external_system_name: Used as an optional system name to appear in the signature log next to the signers who have been assigned the `embed_url_external_id`., defaults to None + :type external_system_name: Optional[str], optional + """ + super().__init__( + is_document_preparation_needed=is_document_preparation_needed, + redirect_url=redirect_url, + declined_redirect_url=declined_redirect_url, + are_text_signatures_enabled=are_text_signatures_enabled, + email_subject=email_subject, + email_message=email_message, + are_reminders_enabled=are_reminders_enabled, + name=name, + prefill_tags=prefill_tags, + days_valid=days_valid, + external_id=external_id, + template_id=template_id, + external_system_name=external_system_name, + **kwargs + ) + self.type = type + self.source_files = source_files + self.signers = signers + self.signature_color = signature_color + self.id = id + self.prepare_url = prepare_url + self.signing_log = signing_log + self.status = status + self.sign_files = sign_files + self.auto_expire_at = auto_expire_at + self.parent_folder = parent_folder + self.collaborator_level = collaborator_level + self.sender_email = sender_email + self.sender_id = sender_id diff --git a/box_sdk_gen/schemas/sign_request_base.py b/box_sdk_gen/schemas/sign_request_base.py new file mode 100644 index 000000000..181c97249 --- /dev/null +++ b/box_sdk_gen/schemas/sign_request_base.py @@ -0,0 +1,72 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.sign_request_prefill_tag import SignRequestPrefillTag + +from box_sdk_gen.box.errors import BoxSDKError + + +class SignRequestBase(BaseObject): + def __init__( + self, + *, + is_document_preparation_needed: Optional[bool] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + are_text_signatures_enabled: Optional[bool] = None, + email_subject: Optional[str] = None, + email_message: Optional[str] = None, + are_reminders_enabled: Optional[bool] = None, + name: Optional[str] = None, + prefill_tags: Optional[List[SignRequestPrefillTag]] = None, + days_valid: Optional[int] = None, + external_id: Optional[str] = None, + template_id: Optional[str] = None, + external_system_name: Optional[str] = None, + **kwargs + ): + """ + :param is_document_preparation_needed: Indicates if the sender should receive a `prepare_url` in the response to complete document preparation using the UI., defaults to None + :type is_document_preparation_needed: Optional[bool], optional + :param redirect_url: When specified, the signature request will be redirected to this url when a document is signed., defaults to None + :type redirect_url: Optional[str], optional + :param declined_redirect_url: The uri that a signer will be redirected to after declining to sign a document., defaults to None + :type declined_redirect_url: Optional[str], optional + :param are_text_signatures_enabled: Disables the usage of signatures generated by typing (text)., defaults to None + :type are_text_signatures_enabled: Optional[bool], optional + :param email_subject: Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used., defaults to None + :type email_subject: Optional[str], optional + :param email_message: Message to include in sign request email. The field is cleaned through sanitization of specific characters. However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email. The message may contain the following html tags including `a`, `abbr`, `acronym`, `b`, `blockquote`, `code`, `em`, `i`, `ul`, `li`, `ol`, and `strong`. Be aware that when the text to html ratio is too high, the email may end up in spam filters. Custom styles on these tags are not allowed. If this field is not passed, a default message will be used., defaults to None + :type email_message: Optional[str], optional + :param are_reminders_enabled: Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers., defaults to None + :type are_reminders_enabled: Optional[bool], optional + :param name: Name of the signature request., defaults to None + :type name: Optional[str], optional + :param prefill_tags: When a document contains sign-related tags in the content, you can prefill them using this `prefill_tags` by referencing the 'id' of the tag as the `external_id` field of the prefill tag., defaults to None + :type prefill_tags: Optional[List[SignRequestPrefillTag]], optional + :param days_valid: Set the number of days after which the created signature request will automatically expire if not completed. By default, we do not apply any expiration date on signature requests, and the signature request does not expire., defaults to None + :type days_valid: Optional[int], optional + :param external_id: This can be used to reference an ID in an external system that the sign request is related to., defaults to None + :type external_id: Optional[str], optional + :param template_id: When a signature request is created from a template this field will indicate the id of that template., defaults to None + :type template_id: Optional[str], optional + :param external_system_name: Used as an optional system name to appear in the signature log next to the signers who have been assigned the `embed_url_external_id`., defaults to None + :type external_system_name: Optional[str], optional + """ + super().__init__(**kwargs) + self.is_document_preparation_needed = is_document_preparation_needed + self.redirect_url = redirect_url + self.declined_redirect_url = declined_redirect_url + self.are_text_signatures_enabled = are_text_signatures_enabled + self.email_subject = email_subject + self.email_message = email_message + self.are_reminders_enabled = are_reminders_enabled + self.name = name + self.prefill_tags = prefill_tags + self.days_valid = days_valid + self.external_id = external_id + self.template_id = template_id + self.external_system_name = external_system_name diff --git a/box_sdk_gen/schemas/sign_request_create_request.py b/box_sdk_gen/schemas/sign_request_create_request.py new file mode 100644 index 000000000..8d18ce00b --- /dev/null +++ b/box_sdk_gen/schemas/sign_request_create_request.py @@ -0,0 +1,109 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.sign_request_prefill_tag import SignRequestPrefillTag + +from box_sdk_gen.schemas.sign_request_base import SignRequestBase + +from box_sdk_gen.schemas.file_base import FileBase + +from box_sdk_gen.schemas.sign_request_create_signer import SignRequestCreateSigner + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class SignRequestCreateRequestSignatureColorField(str, Enum): + BLUE = 'blue' + BLACK = 'black' + RED = 'red' + + +class SignRequestCreateRequest(SignRequestBase): + def __init__( + self, + signers: List[SignRequestCreateSigner], + *, + source_files: Optional[List[FileBase]] = None, + signature_color: Optional[SignRequestCreateRequestSignatureColorField] = None, + parent_folder: Optional[FolderMini] = None, + is_document_preparation_needed: Optional[bool] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + are_text_signatures_enabled: Optional[bool] = None, + email_subject: Optional[str] = None, + email_message: Optional[str] = None, + are_reminders_enabled: Optional[bool] = None, + name: Optional[str] = None, + prefill_tags: Optional[List[SignRequestPrefillTag]] = None, + days_valid: Optional[int] = None, + external_id: Optional[str] = None, + template_id: Optional[str] = None, + external_system_name: Optional[str] = None, + **kwargs + ): + """ + :param signers: Array of signers for the signature request. 35 is the + max number of signers permitted. + + **Note**: It may happen that some signers belong to conflicting [segments](r://shield-information-barrier-segment-member) (user groups). + This means that due to the security policies, users are assigned to segments to prevent exchanges or communication that could lead to ethical conflicts. + In such a case, an attempt to send the sign request will result in an error. + + Read more about [segments and ethical walls](https://support.box.com/hc/en-us/articles/9920431507603-Understanding-Information-Barriers#h_01GFVJEHQA06N7XEZ4GCZ9GFAQ). + :type signers: List[SignRequestCreateSigner] + :param source_files: List of files to create a signing document from. This is currently limited to ten files. Only the ID and type fields are required for each file., defaults to None + :type source_files: Optional[List[FileBase]], optional + :param signature_color: Force a specific color for the signature (blue, black, or red)., defaults to None + :type signature_color: Optional[SignRequestCreateRequestSignatureColorField], optional + :param is_document_preparation_needed: Indicates if the sender should receive a `prepare_url` in the response to complete document preparation using the UI., defaults to None + :type is_document_preparation_needed: Optional[bool], optional + :param redirect_url: When specified, the signature request will be redirected to this url when a document is signed., defaults to None + :type redirect_url: Optional[str], optional + :param declined_redirect_url: The uri that a signer will be redirected to after declining to sign a document., defaults to None + :type declined_redirect_url: Optional[str], optional + :param are_text_signatures_enabled: Disables the usage of signatures generated by typing (text)., defaults to None + :type are_text_signatures_enabled: Optional[bool], optional + :param email_subject: Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used., defaults to None + :type email_subject: Optional[str], optional + :param email_message: Message to include in sign request email. The field is cleaned through sanitization of specific characters. However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email. The message may contain the following html tags including `a`, `abbr`, `acronym`, `b`, `blockquote`, `code`, `em`, `i`, `ul`, `li`, `ol`, and `strong`. Be aware that when the text to html ratio is too high, the email may end up in spam filters. Custom styles on these tags are not allowed. If this field is not passed, a default message will be used., defaults to None + :type email_message: Optional[str], optional + :param are_reminders_enabled: Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers., defaults to None + :type are_reminders_enabled: Optional[bool], optional + :param name: Name of the signature request., defaults to None + :type name: Optional[str], optional + :param prefill_tags: When a document contains sign-related tags in the content, you can prefill them using this `prefill_tags` by referencing the 'id' of the tag as the `external_id` field of the prefill tag., defaults to None + :type prefill_tags: Optional[List[SignRequestPrefillTag]], optional + :param days_valid: Set the number of days after which the created signature request will automatically expire if not completed. By default, we do not apply any expiration date on signature requests, and the signature request does not expire., defaults to None + :type days_valid: Optional[int], optional + :param external_id: This can be used to reference an ID in an external system that the sign request is related to., defaults to None + :type external_id: Optional[str], optional + :param template_id: When a signature request is created from a template this field will indicate the id of that template., defaults to None + :type template_id: Optional[str], optional + :param external_system_name: Used as an optional system name to appear in the signature log next to the signers who have been assigned the `embed_url_external_id`., defaults to None + :type external_system_name: Optional[str], optional + """ + super().__init__( + is_document_preparation_needed=is_document_preparation_needed, + redirect_url=redirect_url, + declined_redirect_url=declined_redirect_url, + are_text_signatures_enabled=are_text_signatures_enabled, + email_subject=email_subject, + email_message=email_message, + are_reminders_enabled=are_reminders_enabled, + name=name, + prefill_tags=prefill_tags, + days_valid=days_valid, + external_id=external_id, + template_id=template_id, + external_system_name=external_system_name, + **kwargs + ) + self.signers = signers + self.source_files = source_files + self.signature_color = signature_color + self.parent_folder = parent_folder diff --git a/box_sdk_gen/schemas/sign_request_create_signer.py b/box_sdk_gen/schemas/sign_request_create_signer.py new file mode 100644 index 000000000..ed62e5cb1 --- /dev/null +++ b/box_sdk_gen/schemas/sign_request_create_signer.py @@ -0,0 +1,98 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class SignRequestCreateSignerRoleField(str, Enum): + SIGNER = 'signer' + APPROVER = 'approver' + FINAL_COPY_READER = 'final_copy_reader' + + +class SignRequestCreateSigner(BaseObject): + def __init__( + self, + *, + email: Optional[str] = None, + role: Optional[SignRequestCreateSignerRoleField] = None, + is_in_person: Optional[bool] = None, + order: Optional[int] = None, + embed_url_external_user_id: Optional[str] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + login_required: Optional[bool] = None, + verification_phone_number: Optional[str] = None, + password: Optional[str] = None, + signer_group_id: Optional[str] = None, + suppress_notifications: Optional[bool] = None, + **kwargs + ): + """ + :param email: Email address of the signer. + The email address of the signer is required when making signature requests, except when using templates that are configured to include emails., defaults to None + :type email: Optional[str], optional + :param role: Defines the role of the signer in the signature request. A `signer` + must sign the document and an `approver` must approve the document. A + `final_copy_reader` only receives the final signed document and signing + log., defaults to None + :type role: Optional[SignRequestCreateSignerRoleField], optional + :param is_in_person: Used in combination with an embed URL for a sender. After the + sender signs, they are redirected to the next `in_person` signer., defaults to None + :type is_in_person: Optional[bool], optional + :param order: Order of the signer., defaults to None + :type order: Optional[int], optional + :param embed_url_external_user_id: User ID for the signer in an external application responsible + for authentication when accessing the embed URL., defaults to None + :type embed_url_external_user_id: Optional[str], optional + :param redirect_url: The URL that a signer will be redirected + to after signing a document. Defining this URL + overrides default or global redirect URL + settings for a specific signer. + If no declined redirect URL is specified, + this URL will be used for decline actions as well., defaults to None + :type redirect_url: Optional[str], optional + :param declined_redirect_url: The URL that a signer will be redirect + to after declining to sign a document. + Defining this URL overrides default or global + declined redirect URL settings for a specific signer., defaults to None + :type declined_redirect_url: Optional[str], optional + :param login_required: If set to true, the signer will need to log in to a Box account + before signing the request. If the signer does not have + an existing account, they will have the option to create + a free Box account., defaults to None + :type login_required: Optional[bool], optional + :param verification_phone_number: If set, this phone number will be used to verify the signer + via two-factor authentication before they are able to sign the document. + Cannot be selected in combination with `login_required`., defaults to None + :type verification_phone_number: Optional[str], optional + :param password: If set, the signer is required to enter the password before they are able + to sign a document. This field is write only., defaults to None + :type password: Optional[str], optional + :param signer_group_id: If set, signers who have the same value will be assigned to the same input and to the same signer group. + A signer group is not a Box Group. It is an entity that belongs to a Sign Request and can only be + used/accessed within this Sign Request. A signer group is expected to have more than one signer. + If the provided value is only used for one signer, this value will be ignored and request will be handled + as it was intended for an individual signer. The value provided can be any string and only used to + determine which signers belongs to same group. A successful response will provide a generated UUID value + instead for signers in the same signer group., defaults to None + :type signer_group_id: Optional[str], optional + :param suppress_notifications: If true, no emails about the sign request will be sent., defaults to None + :type suppress_notifications: Optional[bool], optional + """ + super().__init__(**kwargs) + self.email = email + self.role = role + self.is_in_person = is_in_person + self.order = order + self.embed_url_external_user_id = embed_url_external_user_id + self.redirect_url = redirect_url + self.declined_redirect_url = declined_redirect_url + self.login_required = login_required + self.verification_phone_number = verification_phone_number + self.password = password + self.signer_group_id = signer_group_id + self.suppress_notifications = suppress_notifications diff --git a/box_sdk_gen/schemas/sign_request_prefill_tag.py b/box_sdk_gen/schemas/sign_request_prefill_tag.py new file mode 100644 index 000000000..88eb1a90c --- /dev/null +++ b/box_sdk_gen/schemas/sign_request_prefill_tag.py @@ -0,0 +1,34 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import Date + + +class SignRequestPrefillTag(BaseObject): + def __init__( + self, + *, + document_tag_id: Optional[str] = None, + text_value: Optional[str] = None, + checkbox_value: Optional[bool] = None, + date_value: Optional[Date] = None, + **kwargs + ): + """ + :param document_tag_id: This references the ID of a specific tag contained in a file of the signature request., defaults to None + :type document_tag_id: Optional[str], optional + :param text_value: Text prefill value., defaults to None + :type text_value: Optional[str], optional + :param checkbox_value: Checkbox prefill value., defaults to None + :type checkbox_value: Optional[bool], optional + :param date_value: Date prefill value., defaults to None + :type date_value: Optional[Date], optional + """ + super().__init__(**kwargs) + self.document_tag_id = document_tag_id + self.text_value = text_value + self.checkbox_value = checkbox_value + self.date_value = date_value diff --git a/box_sdk_gen/schemas/sign_request_signer.py b/box_sdk_gen/schemas/sign_request_signer.py new file mode 100644 index 000000000..e9575f17e --- /dev/null +++ b/box_sdk_gen/schemas/sign_request_signer.py @@ -0,0 +1,159 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.sign_request_create_signer import ( + SignRequestCreateSignerRoleField, +) + +from box_sdk_gen.schemas.sign_request_create_signer import SignRequestCreateSigner + +from box_sdk_gen.schemas.sign_request_signer_input import SignRequestSignerInput + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class SignRequestSignerSignerDecisionTypeField(str, Enum): + SIGNED = 'signed' + DECLINED = 'declined' + + +class SignRequestSignerSignerDecisionField(BaseObject): + _discriminator = 'type', {'signed', 'declined'} + + def __init__( + self, + *, + type: Optional[SignRequestSignerSignerDecisionTypeField] = None, + finalized_at: Optional[DateTime] = None, + additional_info: Optional[str] = None, + **kwargs + ): + """ + :param type: Type of decision made by the signer., defaults to None + :type type: Optional[SignRequestSignerSignerDecisionTypeField], optional + :param finalized_at: Date and Time that the decision was made., defaults to None + :type finalized_at: Optional[DateTime], optional + :param additional_info: Additional info about the decision, such as the decline reason from the signer., defaults to None + :type additional_info: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.finalized_at = finalized_at + self.additional_info = additional_info + + +class SignRequestSigner(SignRequestCreateSigner): + def __init__( + self, + *, + has_viewed_document: Optional[bool] = None, + signer_decision: Optional[SignRequestSignerSignerDecisionField] = None, + inputs: Optional[List[SignRequestSignerInput]] = None, + embed_url: Optional[str] = None, + iframeable_embed_url: Optional[str] = None, + email: Optional[str] = None, + role: Optional[SignRequestCreateSignerRoleField] = None, + is_in_person: Optional[bool] = None, + order: Optional[int] = None, + embed_url_external_user_id: Optional[str] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + login_required: Optional[bool] = None, + verification_phone_number: Optional[str] = None, + password: Optional[str] = None, + signer_group_id: Optional[str] = None, + suppress_notifications: Optional[bool] = None, + **kwargs + ): + """ + :param has_viewed_document: Set to `true` if the signer views the document., defaults to None + :type has_viewed_document: Optional[bool], optional + :param signer_decision: Final decision made by the signer., defaults to None + :type signer_decision: Optional[SignRequestSignerSignerDecisionField], optional + :param embed_url: URL to direct a signer to for signing., defaults to None + :type embed_url: Optional[str], optional + :param iframeable_embed_url: This URL is specifically designed for + signing documents within an HTML `iframe` tag. + It will be returned in the response + only if the `embed_url_external_user_id` + parameter was passed in the + `create Box Sign request` call., defaults to None + :type iframeable_embed_url: Optional[str], optional + :param email: Email address of the signer. + The email address of the signer is required when making signature requests, except when using templates that are configured to include emails., defaults to None + :type email: Optional[str], optional + :param role: Defines the role of the signer in the signature request. A `signer` + must sign the document and an `approver` must approve the document. A + `final_copy_reader` only receives the final signed document and signing + log., defaults to None + :type role: Optional[SignRequestCreateSignerRoleField], optional + :param is_in_person: Used in combination with an embed URL for a sender. After the + sender signs, they are redirected to the next `in_person` signer., defaults to None + :type is_in_person: Optional[bool], optional + :param order: Order of the signer., defaults to None + :type order: Optional[int], optional + :param embed_url_external_user_id: User ID for the signer in an external application responsible + for authentication when accessing the embed URL., defaults to None + :type embed_url_external_user_id: Optional[str], optional + :param redirect_url: The URL that a signer will be redirected + to after signing a document. Defining this URL + overrides default or global redirect URL + settings for a specific signer. + If no declined redirect URL is specified, + this URL will be used for decline actions as well., defaults to None + :type redirect_url: Optional[str], optional + :param declined_redirect_url: The URL that a signer will be redirect + to after declining to sign a document. + Defining this URL overrides default or global + declined redirect URL settings for a specific signer., defaults to None + :type declined_redirect_url: Optional[str], optional + :param login_required: If set to true, the signer will need to log in to a Box account + before signing the request. If the signer does not have + an existing account, they will have the option to create + a free Box account., defaults to None + :type login_required: Optional[bool], optional + :param verification_phone_number: If set, this phone number will be used to verify the signer + via two-factor authentication before they are able to sign the document. + Cannot be selected in combination with `login_required`., defaults to None + :type verification_phone_number: Optional[str], optional + :param password: If set, the signer is required to enter the password before they are able + to sign a document. This field is write only., defaults to None + :type password: Optional[str], optional + :param signer_group_id: If set, signers who have the same value will be assigned to the same input and to the same signer group. + A signer group is not a Box Group. It is an entity that belongs to a Sign Request and can only be + used/accessed within this Sign Request. A signer group is expected to have more than one signer. + If the provided value is only used for one signer, this value will be ignored and request will be handled + as it was intended for an individual signer. The value provided can be any string and only used to + determine which signers belongs to same group. A successful response will provide a generated UUID value + instead for signers in the same signer group., defaults to None + :type signer_group_id: Optional[str], optional + :param suppress_notifications: If true, no emails about the sign request will be sent., defaults to None + :type suppress_notifications: Optional[bool], optional + """ + super().__init__( + email=email, + role=role, + is_in_person=is_in_person, + order=order, + embed_url_external_user_id=embed_url_external_user_id, + redirect_url=redirect_url, + declined_redirect_url=declined_redirect_url, + login_required=login_required, + verification_phone_number=verification_phone_number, + password=password, + signer_group_id=signer_group_id, + suppress_notifications=suppress_notifications, + **kwargs + ) + self.has_viewed_document = has_viewed_document + self.signer_decision = signer_decision + self.inputs = inputs + self.embed_url = embed_url + self.iframeable_embed_url = iframeable_embed_url diff --git a/box_sdk_gen/schemas/sign_request_signer_input.py b/box_sdk_gen/schemas/sign_request_signer_input.py new file mode 100644 index 000000000..20067e3f3 --- /dev/null +++ b/box_sdk_gen/schemas/sign_request_signer_input.py @@ -0,0 +1,81 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.utils import Date + +from box_sdk_gen.schemas.sign_request_prefill_tag import SignRequestPrefillTag + +from box_sdk_gen.box.errors import BoxSDKError + + +class SignRequestSignerInputTypeField(str, Enum): + SIGNATURE = 'signature' + DATE = 'date' + TEXT = 'text' + CHECKBOX = 'checkbox' + RADIO = 'radio' + DROPDOWN = 'dropdown' + + +class SignRequestSignerInputContentTypeField(str, Enum): + SIGNATURE = 'signature' + INITIAL = 'initial' + STAMP = 'stamp' + DATE = 'date' + CHECKBOX = 'checkbox' + TEXT = 'text' + FULL_NAME = 'full_name' + FIRST_NAME = 'first_name' + LAST_NAME = 'last_name' + COMPANY = 'company' + TITLE = 'title' + EMAIL = 'email' + ATTACHMENT = 'attachment' + RADIO = 'radio' + DROPDOWN = 'dropdown' + + +class SignRequestSignerInput(SignRequestPrefillTag): + def __init__( + self, + page_index: int, + *, + type: Optional[SignRequestSignerInputTypeField] = None, + content_type: Optional[SignRequestSignerInputContentTypeField] = None, + read_only: Optional[bool] = None, + document_tag_id: Optional[str] = None, + text_value: Optional[str] = None, + checkbox_value: Optional[bool] = None, + date_value: Optional[Date] = None, + **kwargs + ): + """ + :param page_index: Index of page that the input is on. + :type page_index: int + :param type: Type of input., defaults to None + :type type: Optional[SignRequestSignerInputTypeField], optional + :param content_type: Content type of input., defaults to None + :type content_type: Optional[SignRequestSignerInputContentTypeField], optional + :param read_only: Whether this input was defined as read-only(immutable by signers) or not., defaults to None + :type read_only: Optional[bool], optional + :param document_tag_id: This references the ID of a specific tag contained in a file of the signature request., defaults to None + :type document_tag_id: Optional[str], optional + :param text_value: Text prefill value., defaults to None + :type text_value: Optional[str], optional + :param checkbox_value: Checkbox prefill value., defaults to None + :type checkbox_value: Optional[bool], optional + :param date_value: Date prefill value., defaults to None + :type date_value: Optional[Date], optional + """ + super().__init__( + document_tag_id=document_tag_id, + text_value=text_value, + checkbox_value=checkbox_value, + date_value=date_value, + **kwargs + ) + self.page_index = page_index + self.type = type + self.content_type = content_type + self.read_only = read_only diff --git a/box_sdk_gen/schemas/sign_requests.py b/box_sdk_gen/schemas/sign_requests.py new file mode 100644 index 000000000..71c88ebf2 --- /dev/null +++ b/box_sdk_gen/schemas/sign_requests.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.sign_request import SignRequest + +from box_sdk_gen.box.errors import BoxSDKError + + +class SignRequests(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + entries: Optional[List[SignRequest]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param entries: A list of Box Sign requests., defaults to None + :type entries: Optional[List[SignRequest]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/sign_template.py b/box_sdk_gen/schemas/sign_template.py new file mode 100644 index 000000000..08f7ca684 --- /dev/null +++ b/box_sdk_gen/schemas/sign_template.py @@ -0,0 +1,251 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.template_signer import TemplateSigner + +from box_sdk_gen.box.errors import BoxSDKError + + +class SignTemplateTypeField(str, Enum): + SIGN_TEMPLATE = 'sign-template' + + +class SignTemplateAdditionalInfoNonEditableField(str, Enum): + EMAIL_SUBJECT = 'email_subject' + EMAIL_MESSAGE = 'email_message' + NAME = 'name' + DAYS_VALID = 'days_valid' + SIGNERS = 'signers' + SOURCE_FILES = 'source_files' + + +class SignTemplateAdditionalInfoRequiredSignersField(str, Enum): + EMAIL = 'email' + + +class SignTemplateAdditionalInfoRequiredField(BaseObject): + def __init__( + self, + *, + signers: Optional[ + List[List[SignTemplateAdditionalInfoRequiredSignersField]] + ] = None, + **kwargs + ): + """ + :param signers: Required signer fields., defaults to None + :type signers: Optional[List[List[SignTemplateAdditionalInfoRequiredSignersField]]], optional + """ + super().__init__(**kwargs) + self.signers = signers + + +class SignTemplateAdditionalInfoField(BaseObject): + def __init__( + self, + *, + non_editable: Optional[List[SignTemplateAdditionalInfoNonEditableField]] = None, + required: Optional[SignTemplateAdditionalInfoRequiredField] = None, + **kwargs + ): + """ + :param non_editable: Non editable fields., defaults to None + :type non_editable: Optional[List[SignTemplateAdditionalInfoNonEditableField]], optional + :param required: Required fields., defaults to None + :type required: Optional[SignTemplateAdditionalInfoRequiredField], optional + """ + super().__init__(**kwargs) + self.non_editable = non_editable + self.required = required + + +class SignTemplateReadySignLinkField(BaseObject): + def __init__( + self, + *, + url: Optional[str] = None, + name: Optional[str] = None, + instructions: Optional[str] = None, + folder_id: Optional[str] = None, + is_notification_disabled: Optional[bool] = None, + is_active: Optional[bool] = None, + **kwargs + ): + """ + :param url: The URL that can be sent to signers., defaults to None + :type url: Optional[str], optional + :param name: Request name., defaults to None + :type name: Optional[str], optional + :param instructions: Extra instructions for all signers., defaults to None + :type instructions: Optional[str], optional + :param folder_id: The destination folder to place final, + signed document and signing + log. Only `ID` and `type` fields are required. + The root folder, + folder ID `0`, cannot be used., defaults to None + :type folder_id: Optional[str], optional + :param is_notification_disabled: Whether to disable notifications when + a signer has signed., defaults to None + :type is_notification_disabled: Optional[bool], optional + :param is_active: Whether the ready sign link is enabled or not., defaults to None + :type is_active: Optional[bool], optional + """ + super().__init__(**kwargs) + self.url = url + self.name = name + self.instructions = instructions + self.folder_id = folder_id + self.is_notification_disabled = is_notification_disabled + self.is_active = is_active + + +class SignTemplateCustomBrandingField(BaseObject): + def __init__( + self, + *, + company_name: Optional[str] = None, + logo_uri: Optional[str] = None, + branding_color: Optional[str] = None, + email_footer_text: Optional[str] = None, + **kwargs + ): + """ + :param company_name: Name of the company., defaults to None + :type company_name: Optional[str], optional + :param logo_uri: Custom branding logo URI in the form of a base64 image., defaults to None + :type logo_uri: Optional[str], optional + :param branding_color: Custom branding color in hex., defaults to None + :type branding_color: Optional[str], optional + :param email_footer_text: Content of the email footer., defaults to None + :type email_footer_text: Optional[str], optional + """ + super().__init__(**kwargs) + self.company_name = company_name + self.logo_uri = logo_uri + self.branding_color = branding_color + self.email_footer_text = email_footer_text + + +class SignTemplate(BaseObject): + _discriminator = 'type', {'sign-template'} + + def __init__( + self, + *, + type: Optional[SignTemplateTypeField] = None, + id: Optional[str] = None, + name: Optional[str] = None, + email_subject: Optional[str] = None, + email_message: Optional[str] = None, + days_valid: Optional[int] = None, + parent_folder: Optional[FolderMini] = None, + source_files: Optional[List[FileMini]] = None, + are_fields_locked: Optional[bool] = None, + are_options_locked: Optional[bool] = None, + are_recipients_locked: Optional[bool] = None, + are_email_settings_locked: Optional[bool] = None, + are_files_locked: Optional[bool] = None, + signers: Optional[List[TemplateSigner]] = None, + additional_info: Optional[SignTemplateAdditionalInfoField] = None, + ready_sign_link: Optional[SignTemplateReadySignLinkField] = None, + custom_branding: Optional[SignTemplateCustomBrandingField] = None, + **kwargs + ): + """ + :param type: The value will always be `sign-template`., defaults to None + :type type: Optional[SignTemplateTypeField], optional + :param id: Template identifier., defaults to None + :type id: Optional[str], optional + :param name: The name of the template., defaults to None + :type name: Optional[str], optional + :param email_subject: Subject of signature request email. This is cleaned by sign + request. If this field is not passed, a default subject will be used., defaults to None + :type email_subject: Optional[str], optional + :param email_message: Message to include in signature request email. The field + is cleaned through sanitization of specific characters. However, + some html tags are allowed. Links included in the + message are also converted to hyperlinks in the email. The + message may contain the following html tags including `a`, `abbr`, + `acronym`, `b`, `blockquote`, `code`, `em`, `i`, `ul`, `li`, `ol`, and + `strong`. Be aware that when the text + to html ratio is too high, the email + may end up in spam filters. Custom styles on + these tags are not allowed. + If this field is not passed, a default message will be used., defaults to None + :type email_message: Optional[str], optional + :param days_valid: Set the number of days after which the + created signature request will automatically + expire if not completed. By default, we do + not apply any expiration date on signature + requests, and the signature request does not expire., defaults to None + :type days_valid: Optional[int], optional + :param source_files: List of files to create a signing document from. + Only the ID and type fields are required + for each file., defaults to None + :type source_files: Optional[List[FileMini]], optional + :param are_fields_locked: Indicates if the template input + fields are editable or not., defaults to None + :type are_fields_locked: Optional[bool], optional + :param are_options_locked: Indicates if the template document options + are editable or not, + for example renaming the document., defaults to None + :type are_options_locked: Optional[bool], optional + :param are_recipients_locked: Indicates if the template signers are editable or not., defaults to None + :type are_recipients_locked: Optional[bool], optional + :param are_email_settings_locked: Indicates if the template email settings are editable or not., defaults to None + :type are_email_settings_locked: Optional[bool], optional + :param are_files_locked: Indicates if the template files are editable or not. + This includes deleting or renaming template files., defaults to None + :type are_files_locked: Optional[bool], optional + :param signers: Array of signers for the template. + + **Note**: It may happen that some signers specified in the template belong to conflicting [segments](r://shield-information-barrier-segment-member) (user groups). + This means that due to the security policies, users are assigned to segments to prevent exchanges or communication that could lead to ethical conflicts. + In such a case, an attempt to send a sign request based on a template that lists signers in conflicting segments will result in an error. + + Read more about [segments and ethical walls](https://support.box.com/hc/en-us/articles/9920431507603-Understanding-Information-Barriers#h_01GFVJEHQA06N7XEZ4GCZ9GFAQ)., defaults to None + :type signers: Optional[List[TemplateSigner]], optional + :param additional_info: Additional information on which fields are + required and which fields are not editable., defaults to None + :type additional_info: Optional[SignTemplateAdditionalInfoField], optional + :param ready_sign_link: Box's ready-sign link feature enables you to create a + link to a signature request that + you've created from a template. Use this link + when you want to post a signature request + on a public form — such as an email, social media post, + or web page — without knowing who the signers will be. + Note: The ready-sign link feature is + limited to Enterprise Plus customers and not + available to Box Verified Enterprises., defaults to None + :type ready_sign_link: Optional[SignTemplateReadySignLinkField], optional + :param custom_branding: Custom branding applied to notifications + and signature requests., defaults to None + :type custom_branding: Optional[SignTemplateCustomBrandingField], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.name = name + self.email_subject = email_subject + self.email_message = email_message + self.days_valid = days_valid + self.parent_folder = parent_folder + self.source_files = source_files + self.are_fields_locked = are_fields_locked + self.are_options_locked = are_options_locked + self.are_recipients_locked = are_recipients_locked + self.are_email_settings_locked = are_email_settings_locked + self.are_files_locked = are_files_locked + self.signers = signers + self.additional_info = additional_info + self.ready_sign_link = ready_sign_link + self.custom_branding = custom_branding diff --git a/box_sdk_gen/schemas/sign_templates.py b/box_sdk_gen/schemas/sign_templates.py new file mode 100644 index 000000000..00f7f744e --- /dev/null +++ b/box_sdk_gen/schemas/sign_templates.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.sign_template import SignTemplate + +from box_sdk_gen.box.errors import BoxSDKError + + +class SignTemplates(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[SignTemplate]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of templates., defaults to None + :type entries: Optional[List[SignTemplate]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/skill_card.py b/box_sdk_gen/schemas/skill_card.py new file mode 100644 index 000000000..8646039f2 --- /dev/null +++ b/box_sdk_gen/schemas/skill_card.py @@ -0,0 +1,15 @@ +from typing import Union + +from box_sdk_gen.schemas.keyword_skill_card import KeywordSkillCard + +from box_sdk_gen.schemas.timeline_skill_card import TimelineSkillCard + +from box_sdk_gen.schemas.transcript_skill_card import TranscriptSkillCard + +from box_sdk_gen.schemas.status_skill_card import StatusSkillCard + +from box_sdk_gen.box.errors import BoxSDKError + +SkillCard = Union[ + KeywordSkillCard, TimelineSkillCard, TranscriptSkillCard, StatusSkillCard +] diff --git a/box_sdk_gen/schemas/skill_cards_metadata.py b/box_sdk_gen/schemas/skill_cards_metadata.py new file mode 100644 index 000000000..5c0b8e819 --- /dev/null +++ b/box_sdk_gen/schemas/skill_cards_metadata.py @@ -0,0 +1,93 @@ +from typing import Optional + +from typing import List + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.keyword_skill_card import KeywordSkillCard + +from box_sdk_gen.schemas.timeline_skill_card import TimelineSkillCard + +from box_sdk_gen.schemas.transcript_skill_card import TranscriptSkillCard + +from box_sdk_gen.schemas.status_skill_card import StatusSkillCard + +from box_sdk_gen.schemas.skill_card import SkillCard + +from box_sdk_gen.box.errors import BoxSDKError + + +class SkillCardsMetadata(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'can_edit': '$canEdit', + 'id': '$id', + 'parent': '$parent', + 'scope': '$scope', + 'template': '$template', + 'type': '$type', + 'type_version': '$typeVersion', + 'version': '$version', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + '$canEdit': 'can_edit', + '$id': 'id', + '$parent': 'parent', + '$scope': 'scope', + '$template': 'template', + '$type': 'type', + '$typeVersion': 'type_version', + '$version': 'version', + **BaseObject._json_to_fields_mapping, + } + + def __init__( + self, + *, + can_edit: Optional[bool] = None, + id: Optional[str] = None, + parent: Optional[str] = None, + scope: Optional[str] = None, + template: Optional[str] = None, + type: Optional[str] = None, + type_version: Optional[int] = None, + version: Optional[int] = None, + cards: Optional[List[SkillCard]] = None, + **kwargs + ): + """ + :param can_edit: Whether the user can edit this metadata., defaults to None + :type can_edit: Optional[bool], optional + :param id: A UUID to identify the metadata object., defaults to None + :type id: Optional[str], optional + :param parent: An ID for the parent folder., defaults to None + :type parent: Optional[str], optional + :param scope: An ID for the scope in which this template + has been applied., defaults to None + :type scope: Optional[str], optional + :param template: The name of the template., defaults to None + :type template: Optional[str], optional + :param type: A unique identifier for the "type" of this instance. This is an internal + system property and should not be used by a client application., defaults to None + :type type: Optional[str], optional + :param type_version: The last-known version of the template of the object. This is an internal + system property and should not be used by a client application., defaults to None + :type type_version: Optional[int], optional + :param version: The version of the metadata object. Starts at 0 and increases every time + a user-defined property is modified., defaults to None + :type version: Optional[int], optional + :param cards: A list of Box Skill cards that have been applied to this file., defaults to None + :type cards: Optional[List[SkillCard]], optional + """ + super().__init__(**kwargs) + self.can_edit = can_edit + self.id = id + self.parent = parent + self.scope = scope + self.template = template + self.type = type + self.type_version = type_version + self.version = version + self.cards = cards diff --git a/box_sdk_gen/schemas/status_skill_card.py b/box_sdk_gen/schemas/status_skill_card.py new file mode 100644 index 000000000..8ae1d2bb3 --- /dev/null +++ b/box_sdk_gen/schemas/status_skill_card.py @@ -0,0 +1,158 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class StatusSkillCardTypeField(str, Enum): + SKILL_CARD = 'skill_card' + + +class StatusSkillCardSkillCardTypeField(str, Enum): + STATUS = 'status' + + +class StatusSkillCardSkillCardTitleField(BaseObject): + def __init__(self, message: str, *, code: Optional[str] = None, **kwargs): + """ + :param message: The actual title to show in the UI. + :type message: str + :param code: An optional identifier for the title., defaults to None + :type code: Optional[str], optional + """ + super().__init__(**kwargs) + self.message = message + self.code = code + + +class StatusSkillCardStatusCodeField(str, Enum): + INVOKED = 'invoked' + PROCESSING = 'processing' + SUCCESS = 'success' + TRANSIENT_FAILURE = 'transient_failure' + PERMANENT_FAILURE = 'permanent_failure' + + +class StatusSkillCardStatusField(BaseObject): + def __init__( + self, + code: StatusSkillCardStatusCodeField, + *, + message: Optional[str] = None, + **kwargs + ): + """ + :param code: A code for the status of this Skill invocation. By + default each of these will have their own accompanied + messages. These can be adjusted by setting the `message` + value on this object. + :type code: StatusSkillCardStatusCodeField + :param message: A custom message that can be provided with this status. + This will be shown in the web app to the end user., defaults to None + :type message: Optional[str], optional + """ + super().__init__(**kwargs) + self.code = code + self.message = message + + +class StatusSkillCardSkillTypeField(str, Enum): + SERVICE = 'service' + + +class StatusSkillCardSkillField(BaseObject): + _discriminator = 'type', {'service'} + + def __init__( + self, + id: str, + *, + type: StatusSkillCardSkillTypeField = StatusSkillCardSkillTypeField.SERVICE, + **kwargs + ): + """ + :param id: A custom identifier that represent the service that + applied this metadata. + :type id: str + :param type: The value will always be `service`., defaults to StatusSkillCardSkillTypeField.SERVICE + :type type: StatusSkillCardSkillTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class StatusSkillCardInvocationTypeField(str, Enum): + SKILL_INVOCATION = 'skill_invocation' + + +class StatusSkillCardInvocationField(BaseObject): + _discriminator = 'type', {'skill_invocation'} + + def __init__( + self, + id: str, + *, + type: StatusSkillCardInvocationTypeField = StatusSkillCardInvocationTypeField.SKILL_INVOCATION, + **kwargs + ): + """ + :param id: A custom identifier that represent the instance of + the service that applied this metadata. For example, + if your `image-recognition-service` runs on multiple + nodes, this field can be used to identify the ID of + the node that was used to apply the metadata. + :type id: str + :param type: The value will always be `skill_invocation`., defaults to StatusSkillCardInvocationTypeField.SKILL_INVOCATION + :type type: StatusSkillCardInvocationTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class StatusSkillCard(BaseObject): + _discriminator = 'skill_card_type', {'status'} + + def __init__( + self, + status: StatusSkillCardStatusField, + skill: StatusSkillCardSkillField, + invocation: StatusSkillCardInvocationField, + *, + created_at: Optional[DateTime] = None, + type: StatusSkillCardTypeField = StatusSkillCardTypeField.SKILL_CARD, + skill_card_type: StatusSkillCardSkillCardTypeField = StatusSkillCardSkillCardTypeField.STATUS, + skill_card_title: Optional[StatusSkillCardSkillCardTitleField] = None, + **kwargs + ): + """ + :param status: Sets the status of the skill. This can be used to show a message to the user while the Skill is processing the data, or if it was not able to process the file. + :type status: StatusSkillCardStatusField + :param skill: The service that applied this metadata. + :type skill: StatusSkillCardSkillField + :param invocation: The invocation of this service, used to track + which instance of a service applied the metadata. + :type invocation: StatusSkillCardInvocationField + :param created_at: The optional date and time this card was created at., defaults to None + :type created_at: Optional[DateTime], optional + :param type: The value will always be `skill_card`., defaults to StatusSkillCardTypeField.SKILL_CARD + :type type: StatusSkillCardTypeField, optional + :param skill_card_type: The value will always be `status`., defaults to StatusSkillCardSkillCardTypeField.STATUS + :type skill_card_type: StatusSkillCardSkillCardTypeField, optional + :param skill_card_title: The title of the card., defaults to None + :type skill_card_title: Optional[StatusSkillCardSkillCardTitleField], optional + """ + super().__init__(**kwargs) + self.status = status + self.skill = skill + self.invocation = invocation + self.created_at = created_at + self.type = type + self.skill_card_type = skill_card_type + self.skill_card_title = skill_card_title diff --git a/box_sdk_gen/schemas/storage_policies.py b/box_sdk_gen/schemas/storage_policies.py new file mode 100644 index 000000000..232f48d88 --- /dev/null +++ b/box_sdk_gen/schemas/storage_policies.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.storage_policy import StoragePolicy + +from box_sdk_gen.box.errors import BoxSDKError + + +class StoragePolicies(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[StoragePolicy]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of storage policies., defaults to None + :type entries: Optional[List[StoragePolicy]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/storage_policy.py b/box_sdk_gen/schemas/storage_policy.py new file mode 100644 index 000000000..eb5e1cf38 --- /dev/null +++ b/box_sdk_gen/schemas/storage_policy.py @@ -0,0 +1,28 @@ +from typing import Optional + +from box_sdk_gen.schemas.storage_policy_mini import StoragePolicyMiniTypeField + +from box_sdk_gen.schemas.storage_policy_mini import StoragePolicyMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class StoragePolicy(StoragePolicyMini): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + type: StoragePolicyMiniTypeField = StoragePolicyMiniTypeField.STORAGE_POLICY, + **kwargs + ): + """ + :param id: The unique identifier for this storage policy. + :type id: str + :param name: A descriptive name of the region., defaults to None + :type name: Optional[str], optional + :param type: The value will always be `storage_policy`., defaults to StoragePolicyMiniTypeField.STORAGE_POLICY + :type type: StoragePolicyMiniTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name diff --git a/box_sdk_gen/schemas/storage_policy_assignment.py b/box_sdk_gen/schemas/storage_policy_assignment.py new file mode 100644 index 000000000..5d53c429b --- /dev/null +++ b/box_sdk_gen/schemas/storage_policy_assignment.py @@ -0,0 +1,53 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.storage_policy_mini import StoragePolicyMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class StoragePolicyAssignmentTypeField(str, Enum): + STORAGE_POLICY_ASSIGNMENT = 'storage_policy_assignment' + + +class StoragePolicyAssignmentAssignedToField(BaseObject): + def __init__( + self, *, id: Optional[str] = None, type: Optional[str] = None, **kwargs + ): + """ + :param id: The unique identifier for this object., defaults to None + :type id: Optional[str], optional + :param type: The type for this object., defaults to None + :type type: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class StoragePolicyAssignment(BaseObject): + _discriminator = 'type', {'storage_policy_assignment'} + + def __init__( + self, + id: str, + *, + type: StoragePolicyAssignmentTypeField = StoragePolicyAssignmentTypeField.STORAGE_POLICY_ASSIGNMENT, + storage_policy: Optional[StoragePolicyMini] = None, + assigned_to: Optional[StoragePolicyAssignmentAssignedToField] = None, + **kwargs + ): + """ + :param id: The unique identifier for a storage policy assignment. + :type id: str + :param type: The value will always be `storage_policy_assignment`., defaults to StoragePolicyAssignmentTypeField.STORAGE_POLICY_ASSIGNMENT + :type type: StoragePolicyAssignmentTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.storage_policy = storage_policy + self.assigned_to = assigned_to diff --git a/box_sdk_gen/schemas/storage_policy_assignments.py b/box_sdk_gen/schemas/storage_policy_assignments.py new file mode 100644 index 000000000..6e409766f --- /dev/null +++ b/box_sdk_gen/schemas/storage_policy_assignments.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.storage_policy_assignment import StoragePolicyAssignment + +from box_sdk_gen.box.errors import BoxSDKError + + +class StoragePolicyAssignments(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[StoragePolicyAssignment]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of storage policy assignments., defaults to None + :type entries: Optional[List[StoragePolicyAssignment]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/storage_policy_mini.py b/box_sdk_gen/schemas/storage_policy_mini.py new file mode 100644 index 000000000..430dc8ba9 --- /dev/null +++ b/box_sdk_gen/schemas/storage_policy_mini.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class StoragePolicyMiniTypeField(str, Enum): + STORAGE_POLICY = 'storage_policy' + + +class StoragePolicyMini(BaseObject): + _discriminator = 'type', {'storage_policy'} + + def __init__( + self, + id: str, + *, + type: StoragePolicyMiniTypeField = StoragePolicyMiniTypeField.STORAGE_POLICY, + **kwargs + ): + """ + :param id: The unique identifier for this storage policy. + :type id: str + :param type: The value will always be `storage_policy`., defaults to StoragePolicyMiniTypeField.STORAGE_POLICY + :type type: StoragePolicyMiniTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/task.py b/box_sdk_gen/schemas/task.py new file mode 100644 index 000000000..fc6b2b75a --- /dev/null +++ b/box_sdk_gen/schemas/task.py @@ -0,0 +1,87 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.task_assignments import TaskAssignments + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TaskTypeField(str, Enum): + TASK = 'task' + + +class TaskActionField(str, Enum): + REVIEW = 'review' + COMPLETE = 'complete' + + +class TaskCompletionRuleField(str, Enum): + ALL_ASSIGNEES = 'all_assignees' + ANY_ASSIGNEE = 'any_assignee' + + +class Task(BaseObject): + _discriminator = 'type', {'task'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[TaskTypeField] = None, + item: Optional[FileMini] = None, + due_at: Optional[DateTime] = None, + action: Optional[TaskActionField] = None, + message: Optional[str] = None, + task_assignment_collection: Optional[TaskAssignments] = None, + is_completed: Optional[bool] = None, + created_by: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + completion_rule: Optional[TaskCompletionRuleField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this task., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `task`., defaults to None + :type type: Optional[TaskTypeField], optional + :param due_at: When the task is due., defaults to None + :type due_at: Optional[DateTime], optional + :param action: The type of task the task assignee will be prompted to + perform., defaults to None + :type action: Optional[TaskActionField], optional + :param message: A message that will be included with the task., defaults to None + :type message: Optional[str], optional + :param is_completed: Whether the task has been completed., defaults to None + :type is_completed: Optional[bool], optional + :param created_at: When the task object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param completion_rule: Defines which assignees need to complete this task before the task + is considered completed. + + * `all_assignees` requires all assignees to review or + approve the task in order for it to be considered completed. + * `any_assignee` accepts any one assignee to review or + approve the task in order for it to be considered completed., defaults to None + :type completion_rule: Optional[TaskCompletionRuleField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.item = item + self.due_at = due_at + self.action = action + self.message = message + self.task_assignment_collection = task_assignment_collection + self.is_completed = is_completed + self.created_by = created_by + self.created_at = created_at + self.completion_rule = completion_rule diff --git a/box_sdk_gen/schemas/task_assignment.py b/box_sdk_gen/schemas/task_assignment.py new file mode 100644 index 000000000..6784ec12e --- /dev/null +++ b/box_sdk_gen/schemas/task_assignment.py @@ -0,0 +1,76 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.file_mini import FileMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TaskAssignmentTypeField(str, Enum): + TASK_ASSIGNMENT = 'task_assignment' + + +class TaskAssignmentResolutionStateField(str, Enum): + COMPLETED = 'completed' + INCOMPLETE = 'incomplete' + APPROVED = 'approved' + REJECTED = 'rejected' + + +class TaskAssignment(BaseObject): + _discriminator = 'type', {'task_assignment'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[TaskAssignmentTypeField] = None, + item: Optional[FileMini] = None, + assigned_to: Optional[UserMini] = None, + message: Optional[str] = None, + completed_at: Optional[DateTime] = None, + assigned_at: Optional[DateTime] = None, + reminded_at: Optional[DateTime] = None, + resolution_state: Optional[TaskAssignmentResolutionStateField] = None, + assigned_by: Optional[UserMini] = None, + **kwargs + ): + """ + :param id: The unique identifier for this task assignment., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `task_assignment`., defaults to None + :type type: Optional[TaskAssignmentTypeField], optional + :param message: A message that will is included with the task + assignment. This is visible to the assigned user in the web and mobile + UI., defaults to None + :type message: Optional[str], optional + :param completed_at: The date at which this task assignment was + completed. This will be `null` if the task is not completed yet., defaults to None + :type completed_at: Optional[DateTime], optional + :param assigned_at: The date at which this task was assigned to the user., defaults to None + :type assigned_at: Optional[DateTime], optional + :param reminded_at: The date at which the assigned user was reminded of this task + assignment., defaults to None + :type reminded_at: Optional[DateTime], optional + :param resolution_state: The current state of the assignment. The available states depend on + the `action` value of the task object., defaults to None + :type resolution_state: Optional[TaskAssignmentResolutionStateField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.item = item + self.assigned_to = assigned_to + self.message = message + self.completed_at = completed_at + self.assigned_at = assigned_at + self.reminded_at = reminded_at + self.resolution_state = resolution_state + self.assigned_by = assigned_by diff --git a/box_sdk_gen/schemas/task_assignments.py b/box_sdk_gen/schemas/task_assignments.py new file mode 100644 index 000000000..66c3937f5 --- /dev/null +++ b/box_sdk_gen/schemas/task_assignments.py @@ -0,0 +1,28 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.task_assignment import TaskAssignment + +from box_sdk_gen.box.errors import BoxSDKError + + +class TaskAssignments(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + entries: Optional[List[TaskAssignment]] = None, + **kwargs + ): + """ + :param total_count: The total number of items in this collection., defaults to None + :type total_count: Optional[int], optional + :param entries: A list of task assignments., defaults to None + :type entries: Optional[List[TaskAssignment]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries diff --git a/box_sdk_gen/schemas/tasks.py b/box_sdk_gen/schemas/tasks.py new file mode 100644 index 000000000..0fd54f38a --- /dev/null +++ b/box_sdk_gen/schemas/tasks.py @@ -0,0 +1,30 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.task import Task + +from box_sdk_gen.box.errors import BoxSDKError + + +class Tasks(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + entries: Optional[List[Task]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`., defaults to None + :type total_count: Optional[int], optional + :param entries: A list of tasks., defaults to None + :type entries: Optional[List[Task]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries diff --git a/box_sdk_gen/schemas/template_signer.py b/box_sdk_gen/schemas/template_signer.py new file mode 100644 index 000000000..be1404779 --- /dev/null +++ b/box_sdk_gen/schemas/template_signer.py @@ -0,0 +1,80 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.template_signer_input import TemplateSignerInput + +from box_sdk_gen.box.errors import BoxSDKError + + +class TemplateSignerRoleField(str, Enum): + SIGNER = 'signer' + APPROVER = 'approver' + FINAL_COPY_READER = 'final_copy_reader' + + +class TemplateSigner(BaseObject): + def __init__( + self, + *, + inputs: Optional[List[TemplateSignerInput]] = None, + email: Optional[str] = None, + role: Optional[TemplateSignerRoleField] = None, + is_in_person: Optional[bool] = None, + order: Optional[int] = None, + signer_group_id: Optional[str] = None, + label: Optional[str] = None, + public_id: Optional[str] = None, + is_password_required: Optional[bool] = None, + is_phone_number_required: Optional[bool] = None, + login_required: Optional[bool] = None, + **kwargs + ): + """ + :param email: Email address of the signer., defaults to None + :type email: Optional[str], optional + :param role: Defines the role of the signer in the signature request. A role of + `signer` needs to sign the document, a role `approver` + approves the document and + a `final_copy_reader` role only + receives the final signed document and signing log., defaults to None + :type role: Optional[TemplateSignerRoleField], optional + :param is_in_person: Used in combination with an embed URL for a sender. + After the sender signs, they will be + redirected to the next `in_person` signer., defaults to None + :type is_in_person: Optional[bool], optional + :param order: Order of the signer., defaults to None + :type order: Optional[int], optional + :param signer_group_id: If provided, this value points signers that are assigned the same inputs and belongs to same signer group. + A signer group is not a Box Group. It is an entity that belongs to the template itself and can only be used + within Box Sign requests created from it., defaults to None + :type signer_group_id: Optional[str], optional + :param label: A placeholder label for the signer set by the template creator to differentiate between signers., defaults to None + :type label: Optional[str], optional + :param public_id: An identifier for the signer. This can be used to identify a signer within the template., defaults to None + :type public_id: Optional[str], optional + :param is_password_required: If true for signers with a defined email, the password provided when the template was created is used by default. + If true for signers without a specified / defined email, the creator needs to provide a password when using the template., defaults to None + :type is_password_required: Optional[bool], optional + :param is_phone_number_required: If true for signers with a defined email, the phone number provided when the template was created is used by default. + If true for signers without a specified / defined email, the template creator needs to provide a phone number when creating a request., defaults to None + :type is_phone_number_required: Optional[bool], optional + :param login_required: If true, the signer is required to login to access the document., defaults to None + :type login_required: Optional[bool], optional + """ + super().__init__(**kwargs) + self.inputs = inputs + self.email = email + self.role = role + self.is_in_person = is_in_person + self.order = order + self.signer_group_id = signer_group_id + self.label = label + self.public_id = public_id + self.is_password_required = is_password_required + self.is_phone_number_required = is_phone_number_required + self.login_required = login_required diff --git a/box_sdk_gen/schemas/template_signer_input.py b/box_sdk_gen/schemas/template_signer_input.py new file mode 100644 index 000000000..32702404a --- /dev/null +++ b/box_sdk_gen/schemas/template_signer_input.py @@ -0,0 +1,147 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.internal.utils import Date + +from box_sdk_gen.schemas.sign_request_prefill_tag import SignRequestPrefillTag + +from box_sdk_gen.box.errors import BoxSDKError + + +class TemplateSignerInputTypeField(str, Enum): + SIGNATURE = 'signature' + DATE = 'date' + TEXT = 'text' + CHECKBOX = 'checkbox' + ATTACHMENT = 'attachment' + RADIO = 'radio' + DROPDOWN = 'dropdown' + + +class TemplateSignerInputContentTypeField(str, Enum): + SIGNATURE = 'signature' + INITIAL = 'initial' + STAMP = 'stamp' + DATE = 'date' + CHECKBOX = 'checkbox' + TEXT = 'text' + FULL_NAME = 'full_name' + FIRST_NAME = 'first_name' + LAST_NAME = 'last_name' + COMPANY = 'company' + TITLE = 'title' + EMAIL = 'email' + ATTACHMENT = 'attachment' + RADIO = 'radio' + DROPDOWN = 'dropdown' + + +class TemplateSignerInputCoordinatesField(BaseObject): + def __init__( + self, *, x: Optional[float] = None, y: Optional[float] = None, **kwargs + ): + """ + :param x: Relative x coordinate to the page the input is on, ranging from 0 to 1., defaults to None + :type x: Optional[float], optional + :param y: Relative y coordinate to the page the input is on, ranging from 0 to 1., defaults to None + :type y: Optional[float], optional + """ + super().__init__(**kwargs) + self.x = x + self.y = y + + +class TemplateSignerInputDimensionsField(BaseObject): + def __init__( + self, *, width: Optional[float] = None, height: Optional[float] = None, **kwargs + ): + """ + :param width: Relative width to the page the input is on, ranging from 0 to 1., defaults to None + :type width: Optional[float], optional + :param height: Relative height to the page the input is on, ranging from 0 to 1., defaults to None + :type height: Optional[float], optional + """ + super().__init__(**kwargs) + self.width = width + self.height = height + + +class TemplateSignerInput(SignRequestPrefillTag): + def __init__( + self, + page_index: int, + *, + type: Optional[TemplateSignerInputTypeField] = None, + content_type: Optional[TemplateSignerInputContentTypeField] = None, + is_required: Optional[bool] = None, + document_id: Optional[str] = None, + dropdown_choices: Optional[List[str]] = None, + group_id: Optional[str] = None, + coordinates: Optional[TemplateSignerInputCoordinatesField] = None, + dimensions: Optional[TemplateSignerInputDimensionsField] = None, + label: Optional[str] = None, + read_only: Optional[bool] = None, + document_tag_id: Optional[str] = None, + text_value: Optional[str] = None, + checkbox_value: Optional[bool] = None, + date_value: Optional[Date] = None, + **kwargs + ): + """ + :param page_index: Index of page that the input is on. + :type page_index: int + :param type: Type of input., defaults to None + :type type: Optional[TemplateSignerInputTypeField], optional + :param content_type: Content type of input., defaults to None + :type content_type: Optional[TemplateSignerInputContentTypeField], optional + :param is_required: Whether or not the input is required., defaults to None + :type is_required: Optional[bool], optional + :param document_id: Document identifier., defaults to None + :type document_id: Optional[str], optional + :param dropdown_choices: When the input is of the type `dropdown` this + values will be filled with all the + dropdown options., defaults to None + :type dropdown_choices: Optional[List[str]], optional + :param group_id: When the input is of type `radio` they can be + grouped to gather with this identifier., defaults to None + :type group_id: Optional[str], optional + :param coordinates: Where the input is located on a page., defaults to None + :type coordinates: Optional[TemplateSignerInputCoordinatesField], optional + :param dimensions: The size of the input., defaults to None + :type dimensions: Optional[TemplateSignerInputDimensionsField], optional + :param label: The label field is used especially for text, attachment, radio, and checkbox type inputs., defaults to None + :type label: Optional[str], optional + :param read_only: Whether this input was defined as read-only(immutable by signers) or not., defaults to None + :type read_only: Optional[bool], optional + :param document_tag_id: This references the ID of a specific tag contained in a file of the signature request., defaults to None + :type document_tag_id: Optional[str], optional + :param text_value: Text prefill value., defaults to None + :type text_value: Optional[str], optional + :param checkbox_value: Checkbox prefill value., defaults to None + :type checkbox_value: Optional[bool], optional + :param date_value: Date prefill value., defaults to None + :type date_value: Optional[Date], optional + """ + super().__init__( + document_tag_id=document_tag_id, + text_value=text_value, + checkbox_value=checkbox_value, + date_value=date_value, + **kwargs + ) + self.page_index = page_index + self.type = type + self.content_type = content_type + self.is_required = is_required + self.document_id = document_id + self.dropdown_choices = dropdown_choices + self.group_id = group_id + self.coordinates = coordinates + self.dimensions = dimensions + self.label = label + self.read_only = read_only diff --git a/box_sdk_gen/schemas/terms_of_service.py b/box_sdk_gen/schemas/terms_of_service.py new file mode 100644 index 000000000..f36ac992c --- /dev/null +++ b/box_sdk_gen/schemas/terms_of_service.py @@ -0,0 +1,92 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.terms_of_service_base import TermsOfServiceBaseTypeField + +from box_sdk_gen.schemas.terms_of_service_base import TermsOfServiceBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TermsOfServiceStatusField(str, Enum): + ENABLED = 'enabled' + DISABLED = 'disabled' + + +class TermsOfServiceEnterpriseTypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class TermsOfServiceEnterpriseField(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[TermsOfServiceEnterpriseTypeField] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this enterprise., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[TermsOfServiceEnterpriseTypeField], optional + :param name: The name of the enterprise., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + + +class TermsOfServiceTosTypeField(str, Enum): + MANAGED = 'managed' + EXTERNAL = 'external' + + +class TermsOfService(TermsOfServiceBase): + def __init__( + self, + id: str, + *, + status: Optional[TermsOfServiceStatusField] = None, + enterprise: Optional[TermsOfServiceEnterpriseField] = None, + tos_type: Optional[TermsOfServiceTosTypeField] = None, + text: Optional[str] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + type: TermsOfServiceBaseTypeField = TermsOfServiceBaseTypeField.TERMS_OF_SERVICE, + **kwargs + ): + """ + :param id: The unique identifier for this terms of service. + :type id: str + :param status: Whether these terms are enabled or not., defaults to None + :type status: Optional[TermsOfServiceStatusField], optional + :param tos_type: Whether to apply these terms to managed users or external users., defaults to None + :type tos_type: Optional[TermsOfServiceTosTypeField], optional + :param text: The text for your terms and conditions. This text could be + empty if the `status` is set to `disabled`., defaults to None + :type text: Optional[str], optional + :param created_at: When the legal item was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the legal item was modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param type: The value will always be `terms_of_service`., defaults to TermsOfServiceBaseTypeField.TERMS_OF_SERVICE + :type type: TermsOfServiceBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.status = status + self.enterprise = enterprise + self.tos_type = tos_type + self.text = text + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/terms_of_service_base.py b/box_sdk_gen/schemas/terms_of_service_base.py new file mode 100644 index 000000000..d2cb2b8f3 --- /dev/null +++ b/box_sdk_gen/schemas/terms_of_service_base.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class TermsOfServiceBaseTypeField(str, Enum): + TERMS_OF_SERVICE = 'terms_of_service' + + +class TermsOfServiceBase(BaseObject): + _discriminator = 'type', {'terms_of_service'} + + def __init__( + self, + id: str, + *, + type: TermsOfServiceBaseTypeField = TermsOfServiceBaseTypeField.TERMS_OF_SERVICE, + **kwargs + ): + """ + :param id: The unique identifier for this terms of service. + :type id: str + :param type: The value will always be `terms_of_service`., defaults to TermsOfServiceBaseTypeField.TERMS_OF_SERVICE + :type type: TermsOfServiceBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/terms_of_service_user_status.py b/box_sdk_gen/schemas/terms_of_service_user_status.py new file mode 100644 index 000000000..d9a8dc3ed --- /dev/null +++ b/box_sdk_gen/schemas/terms_of_service_user_status.py @@ -0,0 +1,54 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.terms_of_service_base import TermsOfServiceBase + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TermsOfServiceUserStatusTypeField(str, Enum): + TERMS_OF_SERVICE_USER_STATUS = 'terms_of_service_user_status' + + +class TermsOfServiceUserStatus(BaseObject): + _discriminator = 'type', {'terms_of_service_user_status'} + + def __init__( + self, + id: str, + *, + type: TermsOfServiceUserStatusTypeField = TermsOfServiceUserStatusTypeField.TERMS_OF_SERVICE_USER_STATUS, + tos: Optional[TermsOfServiceBase] = None, + user: Optional[UserMini] = None, + is_accepted: Optional[bool] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param id: The unique identifier for this terms of service user status. + :type id: str + :param type: The value will always be `terms_of_service_user_status`., defaults to TermsOfServiceUserStatusTypeField.TERMS_OF_SERVICE_USER_STATUS + :type type: TermsOfServiceUserStatusTypeField, optional + :param is_accepted: If the user has accepted the terms of services., defaults to None + :type is_accepted: Optional[bool], optional + :param created_at: When the legal item was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the legal item was modified., defaults to None + :type modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.tos = tos + self.user = user + self.is_accepted = is_accepted + self.created_at = created_at + self.modified_at = modified_at diff --git a/box_sdk_gen/schemas/terms_of_service_user_statuses.py b/box_sdk_gen/schemas/terms_of_service_user_statuses.py new file mode 100644 index 000000000..5438133b1 --- /dev/null +++ b/box_sdk_gen/schemas/terms_of_service_user_statuses.py @@ -0,0 +1,28 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.terms_of_service_user_status import TermsOfServiceUserStatus + +from box_sdk_gen.box.errors import BoxSDKError + + +class TermsOfServiceUserStatuses(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + entries: Optional[List[TermsOfServiceUserStatus]] = None, + **kwargs + ): + """ + :param total_count: The total number of objects., defaults to None + :type total_count: Optional[int], optional + :param entries: A list of terms of service user statuses., defaults to None + :type entries: Optional[List[TermsOfServiceUserStatus]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries diff --git a/box_sdk_gen/schemas/terms_of_services.py b/box_sdk_gen/schemas/terms_of_services.py new file mode 100644 index 000000000..880a1958d --- /dev/null +++ b/box_sdk_gen/schemas/terms_of_services.py @@ -0,0 +1,28 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.terms_of_service import TermsOfService + +from box_sdk_gen.box.errors import BoxSDKError + + +class TermsOfServices(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + entries: Optional[List[TermsOfService]] = None, + **kwargs + ): + """ + :param total_count: The total number of objects., defaults to None + :type total_count: Optional[int], optional + :param entries: A list of terms of service objects., defaults to None + :type entries: Optional[List[TermsOfService]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries diff --git a/box_sdk_gen/schemas/timeline_skill_card.py b/box_sdk_gen/schemas/timeline_skill_card.py new file mode 100644 index 000000000..a08774061 --- /dev/null +++ b/box_sdk_gen/schemas/timeline_skill_card.py @@ -0,0 +1,182 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TimelineSkillCardTypeField(str, Enum): + SKILL_CARD = 'skill_card' + + +class TimelineSkillCardSkillCardTypeField(str, Enum): + TIMELINE = 'timeline' + + +class TimelineSkillCardSkillCardTitleField(BaseObject): + def __init__(self, message: str, *, code: Optional[str] = None, **kwargs): + """ + :param message: The actual title to show in the UI. + :type message: str + :param code: An optional identifier for the title., defaults to None + :type code: Optional[str], optional + """ + super().__init__(**kwargs) + self.message = message + self.code = code + + +class TimelineSkillCardSkillTypeField(str, Enum): + SERVICE = 'service' + + +class TimelineSkillCardSkillField(BaseObject): + _discriminator = 'type', {'service'} + + def __init__( + self, + id: str, + *, + type: TimelineSkillCardSkillTypeField = TimelineSkillCardSkillTypeField.SERVICE, + **kwargs + ): + """ + :param id: A custom identifier that represent the service that + applied this metadata. + :type id: str + :param type: The value will always be `service`., defaults to TimelineSkillCardSkillTypeField.SERVICE + :type type: TimelineSkillCardSkillTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class TimelineSkillCardInvocationTypeField(str, Enum): + SKILL_INVOCATION = 'skill_invocation' + + +class TimelineSkillCardInvocationField(BaseObject): + _discriminator = 'type', {'skill_invocation'} + + def __init__( + self, + id: str, + *, + type: TimelineSkillCardInvocationTypeField = TimelineSkillCardInvocationTypeField.SKILL_INVOCATION, + **kwargs + ): + """ + :param id: A custom identifier that represent the instance of + the service that applied this metadata. For example, + if your `image-recognition-service` runs on multiple + nodes, this field can be used to identify the ID of + the node that was used to apply the metadata. + :type id: str + :param type: The value will always be `skill_invocation`., defaults to TimelineSkillCardInvocationTypeField.SKILL_INVOCATION + :type type: TimelineSkillCardInvocationTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class TimelineSkillCardEntriesAppearsField(BaseObject): + def __init__( + self, *, start: Optional[int] = None, end: Optional[int] = None, **kwargs + ): + """ + :param start: The time in seconds when an + entry should start appearing on a timeline., defaults to None + :type start: Optional[int], optional + :param end: The time in seconds when an + entry should stop appearing on a timeline., defaults to None + :type end: Optional[int], optional + """ + super().__init__(**kwargs) + self.start = start + self.end = end + + +class TimelineSkillCardEntriesField(BaseObject): + def __init__( + self, + *, + text: Optional[str] = None, + appears: Optional[List[TimelineSkillCardEntriesAppearsField]] = None, + image_url: Optional[str] = None, + **kwargs + ): + """ + :param text: The text of the entry. This would be the display + name for an item being placed on the timeline, for example the name + of the person who was detected in a video., defaults to None + :type text: Optional[str], optional + :param appears: Defines a list of timestamps for when this item should appear on the + timeline., defaults to None + :type appears: Optional[List[TimelineSkillCardEntriesAppearsField]], optional + :param image_url: The image to show on a for an entry that appears + on a timeline. This image URL is required for every entry. + + The image will be shown in a + list of items (for example faces), and clicking + the image will show the user where that entry + appears during the duration of this entry., defaults to None + :type image_url: Optional[str], optional + """ + super().__init__(**kwargs) + self.text = text + self.appears = appears + self.image_url = image_url + + +class TimelineSkillCard(BaseObject): + _discriminator = 'skill_card_type', {'timeline'} + + def __init__( + self, + skill: TimelineSkillCardSkillField, + invocation: TimelineSkillCardInvocationField, + entries: List[TimelineSkillCardEntriesField], + *, + created_at: Optional[DateTime] = None, + type: TimelineSkillCardTypeField = TimelineSkillCardTypeField.SKILL_CARD, + skill_card_type: TimelineSkillCardSkillCardTypeField = TimelineSkillCardSkillCardTypeField.TIMELINE, + skill_card_title: Optional[TimelineSkillCardSkillCardTitleField] = None, + duration: Optional[int] = None, + **kwargs + ): + """ + :param skill: The service that applied this metadata. + :type skill: TimelineSkillCardSkillField + :param invocation: The invocation of this service, used to track + which instance of a service applied the metadata. + :type invocation: TimelineSkillCardInvocationField + :param entries: A list of entries on the timeline. + :type entries: List[TimelineSkillCardEntriesField] + :param created_at: The optional date and time this card was created at., defaults to None + :type created_at: Optional[DateTime], optional + :param type: The value will always be `skill_card`., defaults to TimelineSkillCardTypeField.SKILL_CARD + :type type: TimelineSkillCardTypeField, optional + :param skill_card_type: The value will always be `timeline`., defaults to TimelineSkillCardSkillCardTypeField.TIMELINE + :type skill_card_type: TimelineSkillCardSkillCardTypeField, optional + :param skill_card_title: The title of the card., defaults to None + :type skill_card_title: Optional[TimelineSkillCardSkillCardTitleField], optional + :param duration: An total duration in seconds of the timeline., defaults to None + :type duration: Optional[int], optional + """ + super().__init__(**kwargs) + self.skill = skill + self.invocation = invocation + self.entries = entries + self.created_at = created_at + self.type = type + self.skill_card_type = skill_card_type + self.skill_card_title = skill_card_title + self.duration = duration diff --git a/box_sdk_gen/schemas/tracking_code.py b/box_sdk_gen/schemas/tracking_code.py new file mode 100644 index 000000000..0cc016209 --- /dev/null +++ b/box_sdk_gen/schemas/tracking_code.py @@ -0,0 +1,37 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class TrackingCodeTypeField(str, Enum): + TRACKING_CODE = 'tracking_code' + + +class TrackingCode(BaseObject): + _discriminator = 'type', {'tracking_code'} + + def __init__( + self, + *, + type: Optional[TrackingCodeTypeField] = None, + name: Optional[str] = None, + value: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `tracking_code`., defaults to None + :type type: Optional[TrackingCodeTypeField], optional + :param name: The name of the tracking code, which must be preconfigured in + the Admin Console., defaults to None + :type name: Optional[str], optional + :param value: The value of the tracking code., defaults to None + :type value: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.name = name + self.value = value diff --git a/box_sdk_gen/schemas/transcript_skill_card.py b/box_sdk_gen/schemas/transcript_skill_card.py new file mode 100644 index 000000000..43c8742e5 --- /dev/null +++ b/box_sdk_gen/schemas/transcript_skill_card.py @@ -0,0 +1,169 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TranscriptSkillCardTypeField(str, Enum): + SKILL_CARD = 'skill_card' + + +class TranscriptSkillCardSkillCardTypeField(str, Enum): + TRANSCRIPT = 'transcript' + + +class TranscriptSkillCardSkillCardTitleField(BaseObject): + def __init__(self, message: str, *, code: Optional[str] = None, **kwargs): + """ + :param message: The actual title to show in the UI. + :type message: str + :param code: An optional identifier for the title., defaults to None + :type code: Optional[str], optional + """ + super().__init__(**kwargs) + self.message = message + self.code = code + + +class TranscriptSkillCardSkillTypeField(str, Enum): + SERVICE = 'service' + + +class TranscriptSkillCardSkillField(BaseObject): + _discriminator = 'type', {'service'} + + def __init__( + self, + id: str, + *, + type: TranscriptSkillCardSkillTypeField = TranscriptSkillCardSkillTypeField.SERVICE, + **kwargs + ): + """ + :param id: A custom identifier that represent the service that + applied this metadata. + :type id: str + :param type: The value will always be `service`., defaults to TranscriptSkillCardSkillTypeField.SERVICE + :type type: TranscriptSkillCardSkillTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class TranscriptSkillCardInvocationTypeField(str, Enum): + SKILL_INVOCATION = 'skill_invocation' + + +class TranscriptSkillCardInvocationField(BaseObject): + _discriminator = 'type', {'skill_invocation'} + + def __init__( + self, + id: str, + *, + type: TranscriptSkillCardInvocationTypeField = TranscriptSkillCardInvocationTypeField.SKILL_INVOCATION, + **kwargs + ): + """ + :param id: A custom identifier that represent the instance of + the service that applied this metadata. For example, + if your `image-recognition-service` runs on multiple + nodes, this field can be used to identify the ID of + the node that was used to apply the metadata. + :type id: str + :param type: The value will always be `skill_invocation`., defaults to TranscriptSkillCardInvocationTypeField.SKILL_INVOCATION + :type type: TranscriptSkillCardInvocationTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class TranscriptSkillCardEntriesAppearsField(BaseObject): + def __init__(self, *, start: Optional[int] = None, **kwargs): + """ + :param start: The time in seconds when an + entry should start appearing on a timeline., defaults to None + :type start: Optional[int], optional + """ + super().__init__(**kwargs) + self.start = start + + +class TranscriptSkillCardEntriesField(BaseObject): + def __init__( + self, + *, + text: Optional[str] = None, + appears: Optional[List[TranscriptSkillCardEntriesAppearsField]] = None, + **kwargs + ): + """ + :param text: The text of the entry. This would be the transcribed text assigned + to the entry on the timeline., defaults to None + :type text: Optional[str], optional + :param appears: Defines when a transcribed bit of text appears. This only includes a + start time and no end time., defaults to None + :type appears: Optional[List[TranscriptSkillCardEntriesAppearsField]], optional + """ + super().__init__(**kwargs) + self.text = text + self.appears = appears + + +class TranscriptSkillCard(BaseObject): + _discriminator = 'skill_card_type', {'transcript'} + + def __init__( + self, + skill: TranscriptSkillCardSkillField, + invocation: TranscriptSkillCardInvocationField, + entries: List[TranscriptSkillCardEntriesField], + *, + created_at: Optional[DateTime] = None, + type: TranscriptSkillCardTypeField = TranscriptSkillCardTypeField.SKILL_CARD, + skill_card_type: TranscriptSkillCardSkillCardTypeField = TranscriptSkillCardSkillCardTypeField.TRANSCRIPT, + skill_card_title: Optional[TranscriptSkillCardSkillCardTitleField] = None, + duration: Optional[int] = None, + **kwargs + ): + """ + :param skill: The service that applied this metadata. + :type skill: TranscriptSkillCardSkillField + :param invocation: The invocation of this service, used to track + which instance of a service applied the metadata. + :type invocation: TranscriptSkillCardInvocationField + :param entries: An list of entries for the card. This represents the individual entries of + the transcription. + :type entries: List[TranscriptSkillCardEntriesField] + :param created_at: The optional date and time this card was created at., defaults to None + :type created_at: Optional[DateTime], optional + :param type: The value will always be `skill_card`., defaults to TranscriptSkillCardTypeField.SKILL_CARD + :type type: TranscriptSkillCardTypeField, optional + :param skill_card_type: The value will always be `transcript`., defaults to TranscriptSkillCardSkillCardTypeField.TRANSCRIPT + :type skill_card_type: TranscriptSkillCardSkillCardTypeField, optional + :param skill_card_title: The title of the card., defaults to None + :type skill_card_title: Optional[TranscriptSkillCardSkillCardTitleField], optional + :param duration: An optional total duration in seconds. + + Used with a `skill_card_type` of `transcript` or + `timeline`., defaults to None + :type duration: Optional[int], optional + """ + super().__init__(**kwargs) + self.skill = skill + self.invocation = invocation + self.entries = entries + self.created_at = created_at + self.type = type + self.skill_card_type = skill_card_type + self.skill_card_title = skill_card_title + self.duration = duration diff --git a/box_sdk_gen/schemas/trash_file.py b/box_sdk_gen/schemas/trash_file.py new file mode 100644 index 000000000..37374c80d --- /dev/null +++ b/box_sdk_gen/schemas/trash_file.py @@ -0,0 +1,198 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Dict + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TrashFileTypeField(str, Enum): + FILE = 'file' + + +class TrashFilePathCollectionEntriesTypeField(str, Enum): + FOLDER = 'folder' + + +class TrashFilePathCollectionEntriesField(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + *, + type: Optional[TrashFilePathCollectionEntriesTypeField] = None, + id: Optional[str] = None, + sequence_id: Optional[str] = None, + etag: Optional[str] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `folder`., defaults to None + :type type: Optional[TrashFilePathCollectionEntriesTypeField], optional + :param id: The unique identifier that represent a folder., defaults to None + :type id: Optional[str], optional + :param sequence_id: This field is null for the Trash folder., defaults to None + :type sequence_id: Optional[str], optional + :param etag: This field is null for the Trash folder., defaults to None + :type etag: Optional[str], optional + :param name: The name of the Trash folder., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.sequence_id = sequence_id + self.etag = etag + self.name = name + + +class TrashFilePathCollectionField(BaseObject): + def __init__( + self, + total_count: int, + entries: List[TrashFilePathCollectionEntriesField], + **kwargs + ): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: Array of folders for this item's path collection. + :type entries: List[TrashFilePathCollectionEntriesField] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class TrashFileItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class TrashFile(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'sha_1': 'sha1', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'sha1': 'sha_1', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'file'} + + def __init__( + self, + id: str, + sequence_id: str, + sha_1: str, + description: str, + size: int, + path_collection: TrashFilePathCollectionField, + created_at: DateTime, + modified_at: DateTime, + modified_by: UserMini, + owned_by: UserMini, + item_status: TrashFileItemStatusField, + *, + etag: Optional[str] = None, + type: TrashFileTypeField = TrashFileTypeField.FILE, + name: Optional[str] = None, + file_version: Optional[FileVersionMini] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + created_by: Optional[UserMini] = None, + shared_link: Optional[str] = None, + parent: Optional[FolderMini] = None, + **kwargs + ): + """ + :param id: The unique identifier that represent a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + :type id: str + :param sha_1: The SHA1 hash of the file. This can be used to compare the contents + of a file on Box with a local file. + :type sha_1: str + :param description: The optional description of this file. + :type description: str + :param size: The file size in bytes. Be careful parsing this integer as it can + get very large and cause an integer overflow. + :type size: int + :param created_at: The date and time when the file was created on Box. + :type created_at: DateTime + :param modified_at: The date and time when the file was last updated on Box. + :type modified_at: DateTime + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash + * `trashed` when the item has been moved to the trash but not deleted + * `deleted` when the item has been permanently deleted. + :type item_status: TrashFileItemStatusField + :param etag: The HTTP `etag` of this file. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the file if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `file`., defaults to TrashFileTypeField.FILE + :type type: TrashFileTypeField, optional + :param name: The name of the file., defaults to None + :type name: Optional[str], optional + :param trashed_at: The time at which this file was put in the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: The time at which this file is expected to be purged + from the trash., defaults to None + :type purged_at: Optional[DateTime], optional + :param content_created_at: The date and time at which this file was originally + created, which might be before it was uploaded to Box., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this file was last updated, + which might be before it was uploaded to Box., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param shared_link: The shared link for this file. This will + be `null` if a file has been trashed, since the link will no longer + be active., defaults to None + :type shared_link: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.sequence_id = sequence_id + self.sha_1 = sha_1 + self.description = description + self.size = size + self.path_collection = path_collection + self.created_at = created_at + self.modified_at = modified_at + self.modified_by = modified_by + self.owned_by = owned_by + self.item_status = item_status + self.etag = etag + self.type = type + self.name = name + self.file_version = file_version + self.trashed_at = trashed_at + self.purged_at = purged_at + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + self.created_by = created_by + self.shared_link = shared_link + self.parent = parent diff --git a/box_sdk_gen/schemas/trash_file_restored.py b/box_sdk_gen/schemas/trash_file_restored.py new file mode 100644 index 000000000..65051dfa5 --- /dev/null +++ b/box_sdk_gen/schemas/trash_file_restored.py @@ -0,0 +1,157 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.schemas.file_version_mini import FileVersionMini + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TrashFileRestoredTypeField(str, Enum): + FILE = 'file' + + +class TrashFileRestoredPathCollectionField(BaseObject): + def __init__(self, total_count: int, entries: List[FolderMini], **kwargs): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: The parent folders for this item. + :type entries: List[FolderMini] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class TrashFileRestoredItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class TrashFileRestored(BaseObject): + _fields_to_json_mapping: Dict[str, str] = { + 'sha_1': 'sha1', + **BaseObject._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'sha1': 'sha_1', + **BaseObject._json_to_fields_mapping, + } + _discriminator = 'type', {'file'} + + def __init__( + self, + id: str, + sequence_id: str, + sha_1: str, + description: str, + size: int, + path_collection: TrashFileRestoredPathCollectionField, + created_at: DateTime, + modified_at: DateTime, + modified_by: UserMini, + owned_by: UserMini, + item_status: TrashFileRestoredItemStatusField, + *, + etag: Optional[str] = None, + type: TrashFileRestoredTypeField = TrashFileRestoredTypeField.FILE, + name: Optional[str] = None, + file_version: Optional[FileVersionMini] = None, + trashed_at: Optional[str] = None, + purged_at: Optional[str] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + created_by: Optional[UserMini] = None, + shared_link: Optional[str] = None, + parent: Optional[FolderMini] = None, + **kwargs + ): + """ + :param id: The unique identifier that represent a file. + + The ID for any file can be determined + by visiting a file in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/files/123` + the `file_id` is `123`. + :type id: str + :param sha_1: The SHA1 hash of the file. This can be used to compare the contents + of a file on Box with a local file. + :type sha_1: str + :param description: The optional description of this file. + :type description: str + :param size: The file size in bytes. Be careful parsing this integer as it can + get very large and cause an integer overflow. + :type size: int + :param created_at: The date and time when the file was created on Box. + :type created_at: DateTime + :param modified_at: The date and time when the file was last updated on Box. + :type modified_at: DateTime + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash + * `trashed` when the item has been moved to the trash but not deleted + * `deleted` when the item has been permanently deleted. + :type item_status: TrashFileRestoredItemStatusField + :param etag: The HTTP `etag` of this file. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the file if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `file`., defaults to TrashFileRestoredTypeField.FILE + :type type: TrashFileRestoredTypeField, optional + :param name: The name of the file., defaults to None + :type name: Optional[str], optional + :param trashed_at: The time at which this file was put in the + trash - becomes `null` after restore., defaults to None + :type trashed_at: Optional[str], optional + :param purged_at: The time at which this file is expected to be purged + from the trash - becomes `null` after restore., defaults to None + :type purged_at: Optional[str], optional + :param content_created_at: The date and time at which this file was originally + created, which might be before it was uploaded to Box., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this file was last updated, + which might be before it was uploaded to Box., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param shared_link: The shared link for this file. This will + be `null` if a file had been trashed, even though the original shared + link does become active again., defaults to None + :type shared_link: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.sequence_id = sequence_id + self.sha_1 = sha_1 + self.description = description + self.size = size + self.path_collection = path_collection + self.created_at = created_at + self.modified_at = modified_at + self.modified_by = modified_by + self.owned_by = owned_by + self.item_status = item_status + self.etag = etag + self.type = type + self.name = name + self.file_version = file_version + self.trashed_at = trashed_at + self.purged_at = purged_at + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + self.created_by = created_by + self.shared_link = shared_link + self.parent = parent diff --git a/box_sdk_gen/schemas/trash_folder.py b/box_sdk_gen/schemas/trash_folder.py new file mode 100644 index 000000000..cffb89cd1 --- /dev/null +++ b/box_sdk_gen/schemas/trash_folder.py @@ -0,0 +1,188 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TrashFolderTypeField(str, Enum): + FOLDER = 'folder' + + +class TrashFolderPathCollectionEntriesTypeField(str, Enum): + FOLDER = 'folder' + + +class TrashFolderPathCollectionEntriesField(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + *, + type: Optional[TrashFolderPathCollectionEntriesTypeField] = None, + id: Optional[str] = None, + sequence_id: Optional[str] = None, + etag: Optional[str] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `folder`., defaults to None + :type type: Optional[TrashFolderPathCollectionEntriesTypeField], optional + :param id: The unique identifier that represent a folder., defaults to None + :type id: Optional[str], optional + :param sequence_id: This field is null for the Trash folder., defaults to None + :type sequence_id: Optional[str], optional + :param etag: This field is null for the Trash folder., defaults to None + :type etag: Optional[str], optional + :param name: The name of the Trash folder., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.sequence_id = sequence_id + self.etag = etag + self.name = name + + +class TrashFolderPathCollectionField(BaseObject): + def __init__( + self, + total_count: int, + entries: List[TrashFolderPathCollectionEntriesField], + **kwargs + ): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: Array of folders for this item's path collection. + :type entries: List[TrashFolderPathCollectionEntriesField] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class TrashFolderItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class TrashFolder(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + name: str, + description: str, + size: int, + path_collection: TrashFolderPathCollectionField, + created_by: UserMini, + modified_by: UserMini, + owned_by: UserMini, + item_status: TrashFolderItemStatusField, + *, + etag: Optional[str] = None, + type: TrashFolderTypeField = TrashFolderTypeField.FOLDER, + sequence_id: Optional[str] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + shared_link: Optional[str] = None, + folder_upload_email: Optional[str] = None, + parent: Optional[FolderMini] = None, + **kwargs + ): + """ + :param id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting a folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folders/123` + the `folder_id` is `123`. + :type id: str + :param name: The name of the folder. + :type name: str + :param size: The folder size in bytes. + + Be careful parsing this integer as its + value can get very large. + :type size: int + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash + * `trashed` when the item has been moved to the trash but not deleted + * `deleted` when the item has been permanently deleted. + :type item_status: TrashFolderItemStatusField + :param etag: The HTTP `etag` of this folder. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the folder if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `folder`., defaults to TrashFolderTypeField.FOLDER + :type type: TrashFolderTypeField, optional + :param created_at: The date and time when the folder was created. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The date and time when the folder was last updated. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: The time at which this folder was put in the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: The time at which this folder is expected to be purged + from the trash., defaults to None + :type purged_at: Optional[DateTime], optional + :param content_created_at: The date and time at which this folder was originally + created., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this folder was last updated., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param shared_link: The shared link for this folder. This will + be `null` if a folder has been trashed, since the link will no longer + be active., defaults to None + :type shared_link: Optional[str], optional + :param folder_upload_email: The folder upload email for this folder. This will + be `null` if a folder has been trashed, since the upload will no longer + work., defaults to None + :type folder_upload_email: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.name = name + self.description = description + self.size = size + self.path_collection = path_collection + self.created_by = created_by + self.modified_by = modified_by + self.owned_by = owned_by + self.item_status = item_status + self.etag = etag + self.type = type + self.sequence_id = sequence_id + self.created_at = created_at + self.modified_at = modified_at + self.trashed_at = trashed_at + self.purged_at = purged_at + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + self.shared_link = shared_link + self.folder_upload_email = folder_upload_email + self.parent = parent diff --git a/box_sdk_gen/schemas/trash_folder_restored.py b/box_sdk_gen/schemas/trash_folder_restored.py new file mode 100644 index 000000000..5fe75104a --- /dev/null +++ b/box_sdk_gen/schemas/trash_folder_restored.py @@ -0,0 +1,147 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TrashFolderRestoredTypeField(str, Enum): + FOLDER = 'folder' + + +class TrashFolderRestoredPathCollectionField(BaseObject): + def __init__(self, total_count: int, entries: List[FolderMini], **kwargs): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: The parent folders for this item. + :type entries: List[FolderMini] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class TrashFolderRestoredItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class TrashFolderRestored(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + *, + id: Optional[str] = None, + etag: Optional[str] = None, + type: Optional[TrashFolderRestoredTypeField] = None, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + description: Optional[str] = None, + size: Optional[int] = None, + path_collection: Optional[TrashFolderRestoredPathCollectionField] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + trashed_at: Optional[str] = None, + purged_at: Optional[str] = None, + content_created_at: Optional[DateTime] = None, + content_modified_at: Optional[DateTime] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[str] = None, + folder_upload_email: Optional[str] = None, + parent: Optional[FolderMini] = None, + item_status: Optional[TrashFolderRestoredItemStatusField] = None, + **kwargs + ): + """ + :param id: The unique identifier that represent a folder. + + The ID for any folder can be determined + by visiting a folder in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/folders/123` + the `folder_id` is `123`., defaults to None + :type id: Optional[str], optional + :param etag: The HTTP `etag` of this folder. This can be used within some API + endpoints in the `If-Match` and `If-None-Match` headers to only + perform changes on the folder if (no) changes have happened., defaults to None + :type etag: Optional[str], optional + :param type: The value will always be `folder`., defaults to None + :type type: Optional[TrashFolderRestoredTypeField], optional + :param name: The name of the folder., defaults to None + :type name: Optional[str], optional + :param created_at: The date and time when the folder was created. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: The date and time when the folder was last updated. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type modified_at: Optional[DateTime], optional + :param size: The folder size in bytes. + + Be careful parsing this integer as its + value can get very large., defaults to None + :type size: Optional[int], optional + :param trashed_at: The time at which this folder was put in the + trash - becomes `null` after restore., defaults to None + :type trashed_at: Optional[str], optional + :param purged_at: The time at which this folder is expected to be purged + from the trash - becomes `null` after restore., defaults to None + :type purged_at: Optional[str], optional + :param content_created_at: The date and time at which this folder was originally + created., defaults to None + :type content_created_at: Optional[DateTime], optional + :param content_modified_at: The date and time at which this folder was last updated., defaults to None + :type content_modified_at: Optional[DateTime], optional + :param shared_link: The shared link for this file. This will + be `null` if a folder had been trashed, even though the original shared + link does become active again., defaults to None + :type shared_link: Optional[str], optional + :param folder_upload_email: The folder upload email for this folder. This will + be `null` if a folder has been trashed, even though the original upload + email does become active again., defaults to None + :type folder_upload_email: Optional[str], optional + :param item_status: Defines if this item has been deleted or not. + + * `active` when the item has is not in the trash, + * `trashed` when the item has been moved to the trash but not deleted, + * `deleted` when the item has been permanently deleted., defaults to None + :type item_status: Optional[TrashFolderRestoredItemStatusField], optional + """ + super().__init__(**kwargs) + self.id = id + self.etag = etag + self.type = type + self.sequence_id = sequence_id + self.name = name + self.created_at = created_at + self.modified_at = modified_at + self.description = description + self.size = size + self.path_collection = path_collection + self.created_by = created_by + self.modified_by = modified_by + self.trashed_at = trashed_at + self.purged_at = purged_at + self.content_created_at = content_created_at + self.content_modified_at = content_modified_at + self.owned_by = owned_by + self.shared_link = shared_link + self.folder_upload_email = folder_upload_email + self.parent = parent + self.item_status = item_status diff --git a/box_sdk_gen/schemas/trash_web_link.py b/box_sdk_gen/schemas/trash_web_link.py new file mode 100644 index 000000000..5e807b2b7 --- /dev/null +++ b/box_sdk_gen/schemas/trash_web_link.py @@ -0,0 +1,160 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TrashWebLinkTypeField(str, Enum): + WEB_LINK = 'web_link' + + +class TrashWebLinkPathCollectionEntriesTypeField(str, Enum): + FOLDER = 'folder' + + +class TrashWebLinkPathCollectionEntriesField(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + *, + type: Optional[TrashWebLinkPathCollectionEntriesTypeField] = None, + id: Optional[str] = None, + sequence_id: Optional[str] = None, + etag: Optional[str] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `folder`., defaults to None + :type type: Optional[TrashWebLinkPathCollectionEntriesTypeField], optional + :param id: The unique identifier that represent a folder., defaults to None + :type id: Optional[str], optional + :param sequence_id: This field is null for the Trash folder., defaults to None + :type sequence_id: Optional[str], optional + :param etag: This field is null for the Trash folder., defaults to None + :type etag: Optional[str], optional + :param name: The name of the Trash folder., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.sequence_id = sequence_id + self.etag = etag + self.name = name + + +class TrashWebLinkPathCollectionField(BaseObject): + def __init__( + self, + total_count: int, + entries: List[TrashWebLinkPathCollectionEntriesField], + **kwargs + ): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: Array of folders for this item's path collection. + :type entries: List[TrashWebLinkPathCollectionEntriesField] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class TrashWebLinkItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class TrashWebLink(BaseObject): + _discriminator = 'type', {'web_link'} + + def __init__( + self, + *, + type: Optional[TrashWebLinkTypeField] = None, + id: Optional[str] = None, + sequence_id: Optional[str] = None, + etag: Optional[str] = None, + name: Optional[str] = None, + url: Optional[str] = None, + parent: Optional[FolderMini] = None, + description: Optional[str] = None, + path_collection: Optional[TrashWebLinkPathCollectionField] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[str] = None, + item_status: Optional[TrashWebLinkItemStatusField] = None, + **kwargs + ): + """ + :param type: The value will always be `web_link`., defaults to None + :type type: Optional[TrashWebLinkTypeField], optional + :param id: The unique identifier for this web link., defaults to None + :type id: Optional[str], optional + :param etag: The entity tag of this web link. Used with `If-Match` + headers., defaults to None + :type etag: Optional[str], optional + :param name: The name of the web link., defaults to None + :type name: Optional[str], optional + :param url: The URL this web link points to., defaults to None + :type url: Optional[str], optional + :param description: The description accompanying the web link. This is + visible within the Box web application., defaults to None + :type description: Optional[str], optional + :param created_at: When this file was created on Box’s servers., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When this file was last updated on the Box + servers., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: When this file was last moved to the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: When this file will be permanently deleted., defaults to None + :type purged_at: Optional[DateTime], optional + :param shared_link: The shared link for this bookmark. This will + be `null` if a bookmark has been trashed, since the link will no longer + be active., defaults to None + :type shared_link: Optional[str], optional + :param item_status: Whether this item is deleted or not. Values include `active`, + `trashed` if the file has been moved to the trash, and `deleted` if + the file has been permanently deleted., defaults to None + :type item_status: Optional[TrashWebLinkItemStatusField], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.sequence_id = sequence_id + self.etag = etag + self.name = name + self.url = url + self.parent = parent + self.description = description + self.path_collection = path_collection + self.created_at = created_at + self.modified_at = modified_at + self.trashed_at = trashed_at + self.purged_at = purged_at + self.created_by = created_by + self.modified_by = modified_by + self.owned_by = owned_by + self.shared_link = shared_link + self.item_status = item_status diff --git a/box_sdk_gen/schemas/trash_web_link_restored.py b/box_sdk_gen/schemas/trash_web_link_restored.py new file mode 100644 index 000000000..e2f033968 --- /dev/null +++ b/box_sdk_gen/schemas/trash_web_link_restored.py @@ -0,0 +1,120 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class TrashWebLinkRestoredTypeField(str, Enum): + WEB_LINK = 'web_link' + + +class TrashWebLinkRestoredPathCollectionField(BaseObject): + def __init__(self, total_count: int, entries: List[FolderMini], **kwargs): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: The parent folders for this item. + :type entries: List[FolderMini] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class TrashWebLinkRestoredItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class TrashWebLinkRestored(BaseObject): + _discriminator = 'type', {'web_link'} + + def __init__( + self, + sequence_id: str, + path_collection: TrashWebLinkRestoredPathCollectionField, + *, + type: Optional[TrashWebLinkRestoredTypeField] = None, + id: Optional[str] = None, + etag: Optional[str] = None, + name: Optional[str] = None, + url: Optional[str] = None, + parent: Optional[FolderMini] = None, + description: Optional[str] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + trashed_at: Optional[str] = None, + purged_at: Optional[str] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[str] = None, + item_status: Optional[TrashWebLinkRestoredItemStatusField] = None, + **kwargs + ): + """ + :param type: The value will always be `web_link`., defaults to None + :type type: Optional[TrashWebLinkRestoredTypeField], optional + :param id: The unique identifier for this web link., defaults to None + :type id: Optional[str], optional + :param etag: The entity tag of this web link. Used with `If-Match` + headers., defaults to None + :type etag: Optional[str], optional + :param name: The name of the web link., defaults to None + :type name: Optional[str], optional + :param url: The URL this web link points to., defaults to None + :type url: Optional[str], optional + :param description: The description accompanying the web link. This is + visible within the Box web application., defaults to None + :type description: Optional[str], optional + :param created_at: When this file was created on Box’s servers., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When this file was last updated on the Box + servers., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: The time at which this bookmark was put in the + trash - becomes `null` after restore., defaults to None + :type trashed_at: Optional[str], optional + :param purged_at: The time at which this bookmark will be permanently + deleted - becomes `null` after restore., defaults to None + :type purged_at: Optional[str], optional + :param shared_link: The shared link for this bookmark. This will + be `null` if a bookmark had been trashed, even though the original shared + link does become active again., defaults to None + :type shared_link: Optional[str], optional + :param item_status: Whether this item is deleted or not. Values include `active`, + `trashed` if the file has been moved to the trash, and `deleted` if + the file has been permanently deleted., defaults to None + :type item_status: Optional[TrashWebLinkRestoredItemStatusField], optional + """ + super().__init__(**kwargs) + self.sequence_id = sequence_id + self.path_collection = path_collection + self.type = type + self.id = id + self.etag = etag + self.name = name + self.url = url + self.parent = parent + self.description = description + self.created_at = created_at + self.modified_at = modified_at + self.trashed_at = trashed_at + self.purged_at = purged_at + self.created_by = created_by + self.modified_by = modified_by + self.owned_by = owned_by + self.shared_link = shared_link + self.item_status = item_status diff --git a/box_sdk_gen/schemas/upload_part.py b/box_sdk_gen/schemas/upload_part.py new file mode 100644 index 000000000..b9bef8b1d --- /dev/null +++ b/box_sdk_gen/schemas/upload_part.py @@ -0,0 +1,42 @@ +from typing import Optional + +from typing import Dict + +from box_sdk_gen.schemas.upload_part_mini import UploadPartMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class UploadPart(UploadPartMini): + _fields_to_json_mapping: Dict[str, str] = { + 'sha_1': 'sha1', + **UploadPartMini._fields_to_json_mapping, + } + _json_to_fields_mapping: Dict[str, str] = { + 'sha1': 'sha_1', + **UploadPartMini._json_to_fields_mapping, + } + + def __init__( + self, + *, + sha_1: Optional[str] = None, + part_id: Optional[str] = None, + offset: Optional[int] = None, + size: Optional[int] = None, + **kwargs + ): + """ + :param sha_1: The SHA1 hash of the chunk., defaults to None + :type sha_1: Optional[str], optional + :param part_id: The unique ID of the chunk., defaults to None + :type part_id: Optional[str], optional + :param offset: The offset of the chunk within the file + in bytes. The lower bound of the position + of the chunk within the file., defaults to None + :type offset: Optional[int], optional + :param size: The size of the chunk in bytes., defaults to None + :type size: Optional[int], optional + """ + super().__init__(part_id=part_id, offset=offset, size=size, **kwargs) + self.sha_1 = sha_1 diff --git a/box_sdk_gen/schemas/upload_part_mini.py b/box_sdk_gen/schemas/upload_part_mini.py new file mode 100644 index 000000000..70c511ca4 --- /dev/null +++ b/box_sdk_gen/schemas/upload_part_mini.py @@ -0,0 +1,30 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class UploadPartMini(BaseObject): + def __init__( + self, + *, + part_id: Optional[str] = None, + offset: Optional[int] = None, + size: Optional[int] = None, + **kwargs + ): + """ + :param part_id: The unique ID of the chunk., defaults to None + :type part_id: Optional[str], optional + :param offset: The offset of the chunk within the file + in bytes. The lower bound of the position + of the chunk within the file., defaults to None + :type offset: Optional[int], optional + :param size: The size of the chunk in bytes., defaults to None + :type size: Optional[int], optional + """ + super().__init__(**kwargs) + self.part_id = part_id + self.offset = offset + self.size = size diff --git a/box_sdk_gen/schemas/upload_parts.py b/box_sdk_gen/schemas/upload_parts.py new file mode 100644 index 000000000..34c7e69a6 --- /dev/null +++ b/box_sdk_gen/schemas/upload_parts.py @@ -0,0 +1,81 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.upload_part import UploadPart + +from box_sdk_gen.box.errors import BoxSDKError + + +class UploadPartsOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class UploadPartsOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[UploadPartsOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[UploadPartsOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class UploadParts(BaseObject): + def __init__( + self, + *, + total_count: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[UploadPartsOrderField]] = None, + entries: Optional[List[UploadPart]] = None, + **kwargs + ): + """ + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[UploadPartsOrderField]], optional + :param entries: A list of uploaded chunks for an upload + session., defaults to None + :type entries: Optional[List[UploadPart]], optional + """ + super().__init__(**kwargs) + self.total_count = total_count + self.limit = limit + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/upload_session.py b/box_sdk_gen/schemas/upload_session.py new file mode 100644 index 000000000..be0806203 --- /dev/null +++ b/box_sdk_gen/schemas/upload_session.py @@ -0,0 +1,95 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class UploadSessionTypeField(str, Enum): + UPLOAD_SESSION = 'upload_session' + + +class UploadSessionSessionEndpointsField(BaseObject): + def __init__( + self, + *, + upload_part: Optional[str] = None, + commit: Optional[str] = None, + abort: Optional[str] = None, + list_parts: Optional[str] = None, + status: Optional[str] = None, + log_event: Optional[str] = None, + **kwargs + ): + """ + :param upload_part: The URL to upload parts to., defaults to None + :type upload_part: Optional[str], optional + :param commit: The URL used to commit the file., defaults to None + :type commit: Optional[str], optional + :param abort: The URL for used to abort the session., defaults to None + :type abort: Optional[str], optional + :param list_parts: The URL users to list all parts., defaults to None + :type list_parts: Optional[str], optional + :param status: The URL used to get the status of the upload., defaults to None + :type status: Optional[str], optional + :param log_event: The URL used to get the upload log from., defaults to None + :type log_event: Optional[str], optional + """ + super().__init__(**kwargs) + self.upload_part = upload_part + self.commit = commit + self.abort = abort + self.list_parts = list_parts + self.status = status + self.log_event = log_event + + +class UploadSession(BaseObject): + _discriminator = 'type', {'upload_session'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[UploadSessionTypeField] = None, + session_expires_at: Optional[DateTime] = None, + part_size: Optional[int] = None, + total_parts: Optional[int] = None, + num_parts_processed: Optional[int] = None, + session_endpoints: Optional[UploadSessionSessionEndpointsField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this session., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `upload_session`., defaults to None + :type type: Optional[UploadSessionTypeField], optional + :param session_expires_at: The date and time when this session expires., defaults to None + :type session_expires_at: Optional[DateTime], optional + :param part_size: The size in bytes that must be used for all parts of of the + upload. + + Only the last part is allowed to be of a smaller size., defaults to None + :type part_size: Optional[int], optional + :param total_parts: The total number of parts expected in this upload session, + as determined by the file size and part size., defaults to None + :type total_parts: Optional[int], optional + :param num_parts_processed: The number of parts that have been uploaded and processed + by the server. This starts at `0`. + + When committing a file files, inspecting this property can + provide insight if all parts have been uploaded correctly., defaults to None + :type num_parts_processed: Optional[int], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.session_expires_at = session_expires_at + self.part_size = part_size + self.total_parts = total_parts + self.num_parts_processed = num_parts_processed + self.session_endpoints = session_endpoints diff --git a/box_sdk_gen/schemas/upload_url.py b/box_sdk_gen/schemas/upload_url.py new file mode 100644 index 000000000..16ebe3b82 --- /dev/null +++ b/box_sdk_gen/schemas/upload_url.py @@ -0,0 +1,25 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class UploadUrl(BaseObject): + def __init__( + self, + *, + upload_url: Optional[str] = None, + upload_token: Optional[str] = None, + **kwargs + ): + """ + :param upload_url: A URL for an upload session that can be used to upload + the file., defaults to None + :type upload_url: Optional[str], optional + :param upload_token: An optional access token to use to upload the file., defaults to None + :type upload_token: Optional[str], optional + """ + super().__init__(**kwargs) + self.upload_url = upload_url + self.upload_token = upload_token diff --git a/box_sdk_gen/schemas/uploaded_part.py b/box_sdk_gen/schemas/uploaded_part.py new file mode 100644 index 000000000..e8d1fa273 --- /dev/null +++ b/box_sdk_gen/schemas/uploaded_part.py @@ -0,0 +1,13 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.upload_part import UploadPart + +from box_sdk_gen.box.errors import BoxSDKError + + +class UploadedPart(BaseObject): + def __init__(self, *, part: Optional[UploadPart] = None, **kwargs): + super().__init__(**kwargs) + self.part = part diff --git a/box_sdk_gen/schemas/user.py b/box_sdk_gen/schemas/user.py new file mode 100644 index 000000000..58eb548fc --- /dev/null +++ b/box_sdk_gen/schemas/user.py @@ -0,0 +1,120 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.user_base import UserBaseTypeField + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class UserStatusField(str, Enum): + ACTIVE = 'active' + INACTIVE = 'inactive' + CANNOT_DELETE_EDIT = 'cannot_delete_edit' + CANNOT_DELETE_EDIT_UPLOAD = 'cannot_delete_edit_upload' + + +class UserNotificationEmailField(BaseObject): + def __init__( + self, + *, + email: Optional[str] = None, + is_confirmed: Optional[bool] = None, + **kwargs + ): + """ + :param email: The email address to send the notifications to., defaults to None + :type email: Optional[str], optional + :param is_confirmed: Specifies if this email address has been confirmed., defaults to None + :type is_confirmed: Optional[bool], optional + """ + super().__init__(**kwargs) + self.email = email + self.is_confirmed = is_confirmed + + +class User(UserMini): + def __init__( + self, + id: str, + *, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + language: Optional[str] = None, + timezone: Optional[str] = None, + space_amount: Optional[int] = None, + space_used: Optional[int] = None, + max_upload_size: Optional[int] = None, + status: Optional[UserStatusField] = None, + job_title: Optional[str] = None, + phone: Optional[str] = None, + address: Optional[str] = None, + avatar_url: Optional[str] = None, + notification_email: Optional[UserNotificationEmailField] = None, + name: Optional[str] = None, + login: Optional[str] = None, + type: UserBaseTypeField = UserBaseTypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param created_at: When the user object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the user object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param language: The language of the user, formatted in modified version of the + [ISO 639-1](/guides/api-calls/language-codes) format., defaults to None + :type language: Optional[str], optional + :param timezone: The user's timezone., defaults to None + :type timezone: Optional[str], optional + :param space_amount: The user’s total available space amount in bytes., defaults to None + :type space_amount: Optional[int], optional + :param space_used: The amount of space in use by the user., defaults to None + :type space_used: Optional[int], optional + :param max_upload_size: The maximum individual file size in bytes the user can have., defaults to None + :type max_upload_size: Optional[int], optional + :param status: The user's account status., defaults to None + :type status: Optional[UserStatusField], optional + :param job_title: The user’s job title., defaults to None + :type job_title: Optional[str], optional + :param phone: The user’s phone number., defaults to None + :type phone: Optional[str], optional + :param address: The user’s address., defaults to None + :type address: Optional[str], optional + :param avatar_url: URL of the user’s avatar image., defaults to None + :type avatar_url: Optional[str], optional + :param notification_email: An alternate notification email address to which email + notifications are sent. When it's confirmed, this will be + the email address to which notifications are sent instead of + to the primary email address., defaults to None + :type notification_email: Optional[UserNotificationEmailField], optional + :param name: The display name of this user., defaults to None + :type name: Optional[str], optional + :param login: The primary email address of this user., defaults to None + :type login: Optional[str], optional + :param type: The value will always be `user`., defaults to UserBaseTypeField.USER + :type type: UserBaseTypeField, optional + """ + super().__init__(id=id, name=name, login=login, type=type, **kwargs) + self.created_at = created_at + self.modified_at = modified_at + self.language = language + self.timezone = timezone + self.space_amount = space_amount + self.space_used = space_used + self.max_upload_size = max_upload_size + self.status = status + self.job_title = job_title + self.phone = phone + self.address = address + self.avatar_url = avatar_url + self.notification_email = notification_email diff --git a/box_sdk_gen/schemas/user_avatar.py b/box_sdk_gen/schemas/user_avatar.py new file mode 100644 index 000000000..c13545ec1 --- /dev/null +++ b/box_sdk_gen/schemas/user_avatar.py @@ -0,0 +1,38 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserAvatarPicUrlsField(BaseObject): + def __init__( + self, + *, + small: Optional[str] = None, + large: Optional[str] = None, + preview: Optional[str] = None, + **kwargs + ): + """ + :param small: The location of a small-sized avatar., defaults to None + :type small: Optional[str], optional + :param large: The location of a large-sized avatar., defaults to None + :type large: Optional[str], optional + :param preview: The location of the avatar preview., defaults to None + :type preview: Optional[str], optional + """ + super().__init__(**kwargs) + self.small = small + self.large = large + self.preview = preview + + +class UserAvatar(BaseObject): + def __init__(self, *, pic_urls: Optional[UserAvatarPicUrlsField] = None, **kwargs): + """ + :param pic_urls: Represents an object with user avatar URLs., defaults to None + :type pic_urls: Optional[UserAvatarPicUrlsField], optional + """ + super().__init__(**kwargs) + self.pic_urls = pic_urls diff --git a/box_sdk_gen/schemas/user_base.py b/box_sdk_gen/schemas/user_base.py new file mode 100644 index 000000000..a4aefecb8 --- /dev/null +++ b/box_sdk_gen/schemas/user_base.py @@ -0,0 +1,26 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserBaseTypeField(str, Enum): + USER = 'user' + + +class UserBase(BaseObject): + _discriminator = 'type', {'user'} + + def __init__( + self, id: str, *, type: UserBaseTypeField = UserBaseTypeField.USER, **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param type: The value will always be `user`., defaults to UserBaseTypeField.USER + :type type: UserBaseTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/user_collaborations.py b/box_sdk_gen/schemas/user_collaborations.py new file mode 100644 index 000000000..848ce5974 --- /dev/null +++ b/box_sdk_gen/schemas/user_collaborations.py @@ -0,0 +1,36 @@ +from typing import Optional + +from box_sdk_gen.schemas.user_base import UserBaseTypeField + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserCollaborations(UserBase): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + login: Optional[str] = None, + is_active: Optional[bool] = None, + type: UserBaseTypeField = UserBaseTypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param name: The display name of this user. If the collaboration status is `pending`, an empty string is returned., defaults to None + :type name: Optional[str], optional + :param login: The primary email address of this user. If the collaboration status is `pending`, an empty string is returned., defaults to None + :type login: Optional[str], optional + :param is_active: If set to `false`, the user is either deactivated or deleted., defaults to None + :type is_active: Optional[bool], optional + :param type: The value will always be `user`., defaults to UserBaseTypeField.USER + :type type: UserBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name + self.login = login + self.is_active = is_active diff --git a/box_sdk_gen/schemas/user_full.py b/box_sdk_gen/schemas/user_full.py new file mode 100644 index 000000000..b35ae441f --- /dev/null +++ b/box_sdk_gen/schemas/user_full.py @@ -0,0 +1,199 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.user_base import UserBaseTypeField + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.internal.utils import DateTime + +from box_sdk_gen.schemas.user import UserStatusField + +from box_sdk_gen.schemas.user import UserNotificationEmailField + +from box_sdk_gen.schemas.user import User + +from box_sdk_gen.schemas.tracking_code import TrackingCode + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserFullRoleField(str, Enum): + ADMIN = 'admin' + COADMIN = 'coadmin' + USER = 'user' + + +class UserFullEnterpriseTypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class UserFullEnterpriseField(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[UserFullEnterpriseTypeField] = None, + name: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this enterprise., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[UserFullEnterpriseTypeField], optional + :param name: The name of the enterprise., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + + +class UserFull(User): + def __init__( + self, + id: str, + *, + role: Optional[UserFullRoleField] = None, + tracking_codes: Optional[List[TrackingCode]] = None, + can_see_managed_users: Optional[bool] = None, + is_sync_enabled: Optional[bool] = None, + is_external_collab_restricted: Optional[bool] = None, + is_exempt_from_device_limits: Optional[bool] = None, + is_exempt_from_login_verification: Optional[bool] = None, + enterprise: Optional[UserFullEnterpriseField] = None, + my_tags: Optional[List[str]] = None, + hostname: Optional[str] = None, + is_platform_access_only: Optional[bool] = None, + external_app_user_id: Optional[str] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + language: Optional[str] = None, + timezone: Optional[str] = None, + space_amount: Optional[int] = None, + space_used: Optional[int] = None, + max_upload_size: Optional[int] = None, + status: Optional[UserStatusField] = None, + job_title: Optional[str] = None, + phone: Optional[str] = None, + address: Optional[str] = None, + avatar_url: Optional[str] = None, + notification_email: Optional[UserNotificationEmailField] = None, + name: Optional[str] = None, + login: Optional[str] = None, + type: UserBaseTypeField = UserBaseTypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param role: The user’s enterprise role., defaults to None + :type role: Optional[UserFullRoleField], optional + :param tracking_codes: Tracking codes allow an admin to generate reports from the + admin console and assign an attribute to a specific group + of users. This setting must be enabled for an enterprise + before it can be used., defaults to None + :type tracking_codes: Optional[List[TrackingCode]], optional + :param can_see_managed_users: Whether the user can see other enterprise users in their contact list., defaults to None + :type can_see_managed_users: Optional[bool], optional + :param is_sync_enabled: Whether the user can use Box Sync., defaults to None + :type is_sync_enabled: Optional[bool], optional + :param is_external_collab_restricted: Whether the user is allowed to collaborate with users outside their + enterprise., defaults to None + :type is_external_collab_restricted: Optional[bool], optional + :param is_exempt_from_device_limits: Whether to exempt the user from Enterprise device limits., defaults to None + :type is_exempt_from_device_limits: Optional[bool], optional + :param is_exempt_from_login_verification: Whether the user must use two-factor authentication., defaults to None + :type is_exempt_from_login_verification: Optional[bool], optional + :param my_tags: Tags for all files and folders owned by the user. Values returned + will only contain tags that were set by the requester., defaults to None + :type my_tags: Optional[List[str]], optional + :param hostname: The root (protocol, subdomain, domain) of any links that need to be + generated for the user., defaults to None + :type hostname: Optional[str], optional + :param is_platform_access_only: Whether the user is an App User., defaults to None + :type is_platform_access_only: Optional[bool], optional + :param external_app_user_id: An external identifier for an app user, which can be used to look up + the user. This can be used to tie user IDs from external identity + providers to Box users., defaults to None + :type external_app_user_id: Optional[str], optional + :param created_at: When the user object was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When the user object was last modified., defaults to None + :type modified_at: Optional[DateTime], optional + :param language: The language of the user, formatted in modified version of the + [ISO 639-1](/guides/api-calls/language-codes) format., defaults to None + :type language: Optional[str], optional + :param timezone: The user's timezone., defaults to None + :type timezone: Optional[str], optional + :param space_amount: The user’s total available space amount in bytes., defaults to None + :type space_amount: Optional[int], optional + :param space_used: The amount of space in use by the user., defaults to None + :type space_used: Optional[int], optional + :param max_upload_size: The maximum individual file size in bytes the user can have., defaults to None + :type max_upload_size: Optional[int], optional + :param status: The user's account status., defaults to None + :type status: Optional[UserStatusField], optional + :param job_title: The user’s job title., defaults to None + :type job_title: Optional[str], optional + :param phone: The user’s phone number., defaults to None + :type phone: Optional[str], optional + :param address: The user’s address., defaults to None + :type address: Optional[str], optional + :param avatar_url: URL of the user’s avatar image., defaults to None + :type avatar_url: Optional[str], optional + :param notification_email: An alternate notification email address to which email + notifications are sent. When it's confirmed, this will be + the email address to which notifications are sent instead of + to the primary email address., defaults to None + :type notification_email: Optional[UserNotificationEmailField], optional + :param name: The display name of this user., defaults to None + :type name: Optional[str], optional + :param login: The primary email address of this user., defaults to None + :type login: Optional[str], optional + :param type: The value will always be `user`., defaults to UserBaseTypeField.USER + :type type: UserBaseTypeField, optional + """ + super().__init__( + id=id, + created_at=created_at, + modified_at=modified_at, + language=language, + timezone=timezone, + space_amount=space_amount, + space_used=space_used, + max_upload_size=max_upload_size, + status=status, + job_title=job_title, + phone=phone, + address=address, + avatar_url=avatar_url, + notification_email=notification_email, + name=name, + login=login, + type=type, + **kwargs + ) + self.role = role + self.tracking_codes = tracking_codes + self.can_see_managed_users = can_see_managed_users + self.is_sync_enabled = is_sync_enabled + self.is_external_collab_restricted = is_external_collab_restricted + self.is_exempt_from_device_limits = is_exempt_from_device_limits + self.is_exempt_from_login_verification = is_exempt_from_login_verification + self.enterprise = enterprise + self.my_tags = my_tags + self.hostname = hostname + self.is_platform_access_only = is_platform_access_only + self.external_app_user_id = external_app_user_id diff --git a/box_sdk_gen/schemas/user_integration_mappings.py b/box_sdk_gen/schemas/user_integration_mappings.py new file mode 100644 index 000000000..8e14d80f5 --- /dev/null +++ b/box_sdk_gen/schemas/user_integration_mappings.py @@ -0,0 +1,32 @@ +from typing import Optional + +from box_sdk_gen.schemas.user_base import UserBaseTypeField + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserIntegrationMappings(UserBase): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + login: Optional[str] = None, + type: UserBaseTypeField = UserBaseTypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param name: The display name of this user., defaults to None + :type name: Optional[str], optional + :param login: The primary email address of this user., defaults to None + :type login: Optional[str], optional + :param type: The value will always be `user`., defaults to UserBaseTypeField.USER + :type type: UserBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name + self.login = login diff --git a/box_sdk_gen/schemas/user_mini.py b/box_sdk_gen/schemas/user_mini.py new file mode 100644 index 000000000..e53d185b7 --- /dev/null +++ b/box_sdk_gen/schemas/user_mini.py @@ -0,0 +1,32 @@ +from typing import Optional + +from box_sdk_gen.schemas.user_base import UserBaseTypeField + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserMini(UserBase): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + login: Optional[str] = None, + type: UserBaseTypeField = UserBaseTypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param name: The display name of this user., defaults to None + :type name: Optional[str], optional + :param login: The primary email address of this user., defaults to None + :type login: Optional[str], optional + :param type: The value will always be `user`., defaults to UserBaseTypeField.USER + :type type: UserBaseTypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name + self.login = login diff --git a/box_sdk_gen/schemas/users.py b/box_sdk_gen/schemas/users.py new file mode 100644 index 000000000..81c8d6d82 --- /dev/null +++ b/box_sdk_gen/schemas/users.py @@ -0,0 +1,88 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.user_full import UserFull + +from box_sdk_gen.box.errors import BoxSDKError + + +class UsersOrderDirectionField(str, Enum): + ASC = 'ASC' + DESC = 'DESC' + + +class UsersOrderField(BaseObject): + def __init__( + self, + *, + by: Optional[str] = None, + direction: Optional[UsersOrderDirectionField] = None, + **kwargs + ): + """ + :param by: The field to order by., defaults to None + :type by: Optional[str], optional + :param direction: The direction to order by, either ascending or descending., defaults to None + :type direction: Optional[UsersOrderDirectionField], optional + """ + super().__init__(**kwargs) + self.by = by + self.direction = direction + + +class Users(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + total_count: Optional[int] = None, + offset: Optional[int] = None, + order: Optional[List[UsersOrderField]] = None, + entries: Optional[List[UserFull]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param total_count: One greater than the offset of the last entry in the entire collection. + The total number of entries in the collection may be less than + `total_count`. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type total_count: Optional[int], optional + :param offset: The 0-based offset of the first entry in this set. This will be the same + as the `offset` query parameter. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type offset: Optional[int], optional + :param order: The order by which items are returned. + + This field is only returned for calls that use offset-based pagination. + For marker-based paginated APIs, this field will be omitted., defaults to None + :type order: Optional[List[UsersOrderField]], optional + :param entries: A list of users., defaults to None + :type entries: Optional[List[UserFull]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.total_count = total_count + self.offset = offset + self.order = order + self.entries = entries diff --git a/box_sdk_gen/schemas/v2025_r0/__init__.py b/box_sdk_gen/schemas/v2025_r0/__init__.py new file mode 100644 index 000000000..b5405a205 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/__init__.py @@ -0,0 +1,125 @@ +from box_sdk_gen.schemas.v2025_r0.archive_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.archives_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.client_error_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_batch_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_document_generation_data_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_tag_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_tags_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_tags_processing_message_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.enterprise_reference_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.external_user_deletion_result_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.external_users_submit_delete_job_response_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_create_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_templates_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.file_version_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_jobs_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_batch_create_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.folder_reference_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.group_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.group_mini_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_create_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_update_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_copy_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_create_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_item_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_items_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_update_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_mini_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_lists_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_country_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_domain_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_email_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_integration_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_ip_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_lists_update_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_lists_create_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.shield_list_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.terms_of_service_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.user_base_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.user_mini_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hubs_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_user_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_access_grantee_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_collaborations_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_full_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.doc_gen_jobs_full_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.user_reference_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.external_users_submit_delete_job_request_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.weblink_reference_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_item_reference_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_item_operation_result_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_items_manage_response_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_item_operation_v2025_r0 import * + +from box_sdk_gen.schemas.v2025_r0.hub_items_manage_request_v2025_r0 import * diff --git a/box_sdk_gen/schemas/v2025_r0/archive_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/archive_v2025_r0.py new file mode 100644 index 000000000..4c65dafba --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/archive_v2025_r0.py @@ -0,0 +1,43 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ArchiveV2025R0TypeField(str, Enum): + ARCHIVE = 'archive' + + +class ArchiveV2025R0(BaseObject): + _discriminator = 'type', {'archive'} + + def __init__( + self, + id: str, + name: str, + size: int, + *, + type: ArchiveV2025R0TypeField = ArchiveV2025R0TypeField.ARCHIVE, + **kwargs + ): + r""" + :param id: The unique identifier that represents an archive. + :type id: str + :param name: The name of the archive. + + The following restrictions to the archive name apply: names containing + non-printable ASCII characters, forward and backward slashes + (`/`, `\`), names with trailing spaces, and names `.` and `..` are + not allowed. + :type name: str + :param size: The size of the archive in bytes. + :type size: int + :param type: The value will always be `archive`., defaults to ArchiveV2025R0TypeField.ARCHIVE + :type type: ArchiveV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.name = name + self.size = size + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/archives_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/archives_v2025_r0.py new file mode 100644 index 000000000..95c1b67ab --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/archives_v2025_r0.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.archive_v2025_r0 import ArchiveV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class ArchivesV2025R0(BaseObject): + def __init__( + self, + *, + entries: Optional[List[ArchiveV2025R0]] = None, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: A list in which each entry represents an archive object., defaults to None + :type entries: Optional[List[ArchiveV2025R0]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/v2025_r0/client_error_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/client_error_v2025_r0.py new file mode 100644 index 000000000..f081211dd --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/client_error_v2025_r0.py @@ -0,0 +1,77 @@ +from enum import Enum + +from typing import Optional + +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ClientErrorV2025R0TypeField(str, Enum): + ERROR = 'error' + + +class ClientErrorV2025R0CodeField(str, Enum): + CREATED = 'created' + ACCEPTED = 'accepted' + NO_CONTENT = 'no_content' + REDIRECT = 'redirect' + NOT_MODIFIED = 'not_modified' + BAD_REQUEST = 'bad_request' + UNAUTHORIZED = 'unauthorized' + FORBIDDEN = 'forbidden' + NOT_FOUND = 'not_found' + METHOD_NOT_ALLOWED = 'method_not_allowed' + CONFLICT = 'conflict' + PRECONDITION_FAILED = 'precondition_failed' + TOO_MANY_REQUESTS = 'too_many_requests' + INTERNAL_SERVER_ERROR = 'internal_server_error' + UNAVAILABLE = 'unavailable' + ITEM_NAME_INVALID = 'item_name_invalid' + INSUFFICIENT_SCOPE = 'insufficient_scope' + + +class ClientErrorV2025R0(BaseObject): + _discriminator = 'type', {'error'} + + def __init__( + self, + *, + type: Optional[ClientErrorV2025R0TypeField] = None, + status: Optional[int] = None, + code: Optional[ClientErrorV2025R0CodeField] = None, + message: Optional[str] = None, + context_info: Optional[Dict] = None, + help_url: Optional[str] = None, + request_id: Optional[str] = None, + **kwargs + ): + """ + :param type: The value will always be `error`., defaults to None + :type type: Optional[ClientErrorV2025R0TypeField], optional + :param status: The HTTP status of the response., defaults to None + :type status: Optional[int], optional + :param code: A Box-specific error code., defaults to None + :type code: Optional[ClientErrorV2025R0CodeField], optional + :param message: A short message describing the error., defaults to None + :type message: Optional[str], optional + :param context_info: A free-form object that contains additional context + about the error. The possible fields are defined on + a per-endpoint basis. `message` is only one example., defaults to None + :type context_info: Optional[Dict], optional + :param help_url: A URL that links to more information about why this error occurred., defaults to None + :type help_url: Optional[str], optional + :param request_id: A unique identifier for this response, which can be used + when contacting Box support., defaults to None + :type request_id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.status = status + self.code = code + self.message = message + self.context_info = context_info + self.help_url = help_url + self.request_id = request_id diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_batch_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_batch_base_v2025_r0.py new file mode 100644 index 000000000..ff82b370a --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_batch_base_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenBatchBaseV2025R0TypeField(str, Enum): + DOCGEN_BATCH = 'docgen_batch' + + +class DocGenBatchBaseV2025R0(BaseObject): + _discriminator = 'type', {'docgen_batch'} + + def __init__( + self, + id: str, + *, + type: DocGenBatchBaseV2025R0TypeField = DocGenBatchBaseV2025R0TypeField.DOCGEN_BATCH, + **kwargs + ): + """ + :param id: The unique identifier that represents a Box Doc Gen batch. + :type id: str + :param type: The value will always be `docgen_batch`., defaults to DocGenBatchBaseV2025R0TypeField.DOCGEN_BATCH + :type type: DocGenBatchBaseV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_batch_create_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_batch_create_request_v2025_r0.py new file mode 100644 index 000000000..391291724 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_batch_create_request_v2025_r0.py @@ -0,0 +1,71 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.file_version_base_v2025_r0 import ( + FileVersionBaseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_document_generation_data_v2025_r0 import ( + DocGenDocumentGenerationDataV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenBatchCreateRequestV2025R0DestinationFolderTypeField(str, Enum): + FOLDER = 'folder' + + +class DocGenBatchCreateRequestV2025R0DestinationFolderField(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + type: DocGenBatchCreateRequestV2025R0DestinationFolderTypeField = DocGenBatchCreateRequestV2025R0DestinationFolderTypeField.FOLDER, + **kwargs + ): + """ + :param id: ID of the folder. + :type id: str + :param type: The value will always be `folder`., defaults to DocGenBatchCreateRequestV2025R0DestinationFolderTypeField.FOLDER + :type type: DocGenBatchCreateRequestV2025R0DestinationFolderTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class DocGenBatchCreateRequestV2025R0(BaseObject): + def __init__( + self, + file: FileReferenceV2025R0, + input_source: str, + destination_folder: DocGenBatchCreateRequestV2025R0DestinationFolderField, + output_type: str, + document_generation_data: List[DocGenDocumentGenerationDataV2025R0], + *, + file_version: Optional[FileVersionBaseV2025R0] = None, + **kwargs + ): + """ + :param input_source: Source of input. The value has to be `api` for all the API-based document generation requests. + :type input_source: str + :param output_type: Type of the output file. + :type output_type: str + """ + super().__init__(**kwargs) + self.file = file + self.input_source = input_source + self.destination_folder = destination_folder + self.output_type = output_type + self.document_generation_data = document_generation_data + self.file_version = file_version diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_document_generation_data_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_document_generation_data_v2025_r0.py new file mode 100644 index 000000000..b7a70a2a2 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_document_generation_data_v2025_r0.py @@ -0,0 +1,16 @@ +from typing import Dict + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenDocumentGenerationDataV2025R0(BaseObject): + def __init__(self, generated_file_name: str, user_input: Dict, **kwargs): + """ + :param generated_file_name: File name of the output file. + :type generated_file_name: str + """ + super().__init__(**kwargs) + self.generated_file_name = generated_file_name + self.user_input = user_input diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_job_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_job_base_v2025_r0.py new file mode 100644 index 000000000..041ca4c0f --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_job_base_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenJobBaseV2025R0TypeField(str, Enum): + DOCGEN_JOB = 'docgen_job' + + +class DocGenJobBaseV2025R0(BaseObject): + _discriminator = 'type', {'docgen_job'} + + def __init__( + self, + id: str, + *, + type: DocGenJobBaseV2025R0TypeField = DocGenJobBaseV2025R0TypeField.DOCGEN_JOB, + **kwargs + ): + """ + :param id: The unique identifier that represent a Box Doc Gen job. + :type id: str + :param type: The value will always be `docgen_job`., defaults to DocGenJobBaseV2025R0TypeField.DOCGEN_JOB + :type type: DocGenJobBaseV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_job_full_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_job_full_v2025_r0.py new file mode 100644 index 000000000..9debfb02f --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_job_full_v2025_r0.py @@ -0,0 +1,82 @@ +from typing import Optional + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_base_v2025_r0 import ( + DocGenJobBaseV2025R0TypeField, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_base_v2025_r0 import DocGenJobBaseV2025R0 + +from box_sdk_gen.schemas.v2025_r0.doc_gen_batch_base_v2025_r0 import ( + DocGenBatchBaseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.file_version_base_v2025_r0 import ( + FileVersionBaseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_v2025_r0 import ( + DocGenJobV2025R0StatusField, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_v2025_r0 import DocGenJobV2025R0 + +from box_sdk_gen.schemas.v2025_r0.user_base_v2025_r0 import UserBaseV2025R0 + +from box_sdk_gen.schemas.v2025_r0.enterprise_reference_v2025_r0 import ( + EnterpriseReferenceV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenJobFullV2025R0(DocGenJobV2025R0): + def __init__( + self, + created_by: UserBaseV2025R0, + enterprise: EnterpriseReferenceV2025R0, + source: str, + batch: DocGenBatchBaseV2025R0, + template_file: FileReferenceV2025R0, + template_file_version: FileVersionBaseV2025R0, + status: DocGenJobV2025R0StatusField, + output_type: str, + id: str, + *, + created_at: Optional[str] = None, + output_file: Optional[Optional[FileReferenceV2025R0]] = None, + output_file_version: Optional[Optional[FileVersionBaseV2025R0]] = None, + type: DocGenJobBaseV2025R0TypeField = DocGenJobBaseV2025R0TypeField.DOCGEN_JOB, + **kwargs + ): + """ + :param source: Source of the request. + :type source: str + :param status: Status of the job. + :type status: DocGenJobV2025R0StatusField + :param output_type: Type of the generated file. + :type output_type: str + :param id: The unique identifier that represent a Box Doc Gen job. + :type id: str + :param created_at: Time of job creation., defaults to None + :type created_at: Optional[str], optional + :param type: The value will always be `docgen_job`., defaults to DocGenJobBaseV2025R0TypeField.DOCGEN_JOB + :type type: DocGenJobBaseV2025R0TypeField, optional + """ + super().__init__( + batch=batch, + template_file=template_file, + template_file_version=template_file_version, + status=status, + output_type=output_type, + id=id, + output_file=output_file, + output_file_version=output_file_version, + type=type, + **kwargs + ) + self.created_by = created_by + self.enterprise = enterprise + self.source = source + self.created_at = created_at diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_job_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_job_v2025_r0.py new file mode 100644 index 000000000..43e37b347 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_job_v2025_r0.py @@ -0,0 +1,64 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_base_v2025_r0 import ( + DocGenJobBaseV2025R0TypeField, +) + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_base_v2025_r0 import DocGenJobBaseV2025R0 + +from box_sdk_gen.schemas.v2025_r0.doc_gen_batch_base_v2025_r0 import ( + DocGenBatchBaseV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.file_version_base_v2025_r0 import ( + FileVersionBaseV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenJobV2025R0StatusField(str, Enum): + SUBMITTED = 'submitted' + COMPLETED = 'completed' + FAILED = 'failed' + COMPLETED_WITH_ERROR = 'completed_with_error' + PENDING = 'pending' + + +class DocGenJobV2025R0(DocGenJobBaseV2025R0): + def __init__( + self, + batch: DocGenBatchBaseV2025R0, + template_file: FileReferenceV2025R0, + template_file_version: FileVersionBaseV2025R0, + status: DocGenJobV2025R0StatusField, + output_type: str, + id: str, + *, + output_file: Optional[Optional[FileReferenceV2025R0]] = None, + output_file_version: Optional[Optional[FileVersionBaseV2025R0]] = None, + type: DocGenJobBaseV2025R0TypeField = DocGenJobBaseV2025R0TypeField.DOCGEN_JOB, + **kwargs + ): + """ + :param status: Status of the job. + :type status: DocGenJobV2025R0StatusField + :param output_type: Type of the generated file. + :type output_type: str + :param id: The unique identifier that represent a Box Doc Gen job. + :type id: str + :param type: The value will always be `docgen_job`., defaults to DocGenJobBaseV2025R0TypeField.DOCGEN_JOB + :type type: DocGenJobBaseV2025R0TypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.batch = batch + self.template_file = template_file + self.template_file_version = template_file_version + self.status = status + self.output_type = output_type + self.output_file = output_file + self.output_file_version = output_file_version diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_full_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_full_v2025_r0.py new file mode 100644 index 000000000..029beff64 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_full_v2025_r0.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_full_v2025_r0 import DocGenJobFullV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenJobsFullV2025R0(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[DocGenJobFullV2025R0]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: List of jobs., defaults to None + :type entries: Optional[List[DocGenJobFullV2025R0]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_v2025_r0.py new file mode 100644 index 000000000..d5c8d66f9 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_jobs_v2025_r0.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.doc_gen_job_v2025_r0 import DocGenJobV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenJobsV2025R0(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[DocGenJobV2025R0]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: List of jobs., defaults to None + :type entries: Optional[List[DocGenJobV2025R0]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_tag_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_tag_v2025_r0.py new file mode 100644 index 000000000..7b33566ad --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_tag_v2025_r0.py @@ -0,0 +1,38 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenTagV2025R0TagTypeField(str, Enum): + TEXT = 'text' + ARITHMETIC = 'arithmetic' + CONDITIONAL = 'conditional' + FOR_LOOP = 'for-loop' + TABLE_LOOP = 'table-loop' + IMAGE = 'image' + + +class DocGenTagV2025R0(BaseObject): + def __init__( + self, + tag_content: str, + tag_type: DocGenTagV2025R0TagTypeField, + json_paths: List[str], + **kwargs + ): + """ + :param tag_content: The content of the tag. + :type tag_content: str + :param tag_type: Type of the tag. + :type tag_type: DocGenTagV2025R0TagTypeField + :param json_paths: List of the paths. + :type json_paths: List[str] + """ + super().__init__(**kwargs) + self.tag_content = tag_content + self.tag_type = tag_type + self.json_paths = json_paths diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_tags_processing_message_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_tags_processing_message_v2025_r0.py new file mode 100644 index 000000000..00001b17c --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_tags_processing_message_v2025_r0.py @@ -0,0 +1,13 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenTagsProcessingMessageV2025R0(BaseObject): + def __init__(self, message: str, **kwargs): + """ + :param message: A message informing the user that document tags are still being processed. + :type message: str + """ + super().__init__(**kwargs) + self.message = message diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_tags_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_tags_v2025_r0.py new file mode 100644 index 000000000..b51284117 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_tags_v2025_r0.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.doc_gen_tag_v2025_r0 import DocGenTagV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenTagsV2025R0(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[DocGenTagV2025R0]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: List of tags., defaults to None + :type entries: Optional[List[DocGenTagV2025R0]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_template_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_template_base_v2025_r0.py new file mode 100644 index 000000000..aff11476f --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_template_base_v2025_r0.py @@ -0,0 +1,13 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenTemplateBaseV2025R0(BaseObject): + def __init__(self, *, file: Optional[FileReferenceV2025R0] = None, **kwargs): + super().__init__(**kwargs) + self.file = file diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_template_create_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_template_create_request_v2025_r0.py new file mode 100644 index 000000000..9927af5fd --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_template_create_request_v2025_r0.py @@ -0,0 +1,11 @@ +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenTemplateCreateRequestV2025R0(BaseObject): + def __init__(self, file: FileReferenceV2025R0, **kwargs): + super().__init__(**kwargs) + self.file = file diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_template_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_template_v2025_r0.py new file mode 100644 index 000000000..2d4debdbd --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_template_v2025_r0.py @@ -0,0 +1,25 @@ +from typing import Optional + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_base_v2025_r0 import ( + DocGenTemplateBaseV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenTemplateV2025R0(DocGenTemplateBaseV2025R0): + def __init__( + self, + *, + file_name: Optional[str] = None, + file: Optional[FileReferenceV2025R0] = None, + **kwargs + ): + """ + :param file_name: The name of the template., defaults to None + :type file_name: Optional[str], optional + """ + super().__init__(file=file, **kwargs) + self.file_name = file_name diff --git a/box_sdk_gen/schemas/v2025_r0/doc_gen_templates_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/doc_gen_templates_v2025_r0.py new file mode 100644 index 000000000..0bef7118b --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/doc_gen_templates_v2025_r0.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.doc_gen_template_v2025_r0 import DocGenTemplateV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class DocGenTemplatesV2025R0(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[DocGenTemplateV2025R0]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of templates., defaults to None + :type entries: Optional[List[DocGenTemplateV2025R0]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/v2025_r0/enterprise_reference_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/enterprise_reference_v2025_r0.py new file mode 100644 index 000000000..8a55a9250 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/enterprise_reference_v2025_r0.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class EnterpriseReferenceV2025R0TypeField(str, Enum): + ENTERPRISE = 'enterprise' + + +class EnterpriseReferenceV2025R0(BaseObject): + _discriminator = 'type', {'enterprise'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[EnterpriseReferenceV2025R0TypeField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this enterprise., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `enterprise`., defaults to None + :type type: Optional[EnterpriseReferenceV2025R0TypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/external_user_deletion_result_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/external_user_deletion_result_v2025_r0.py new file mode 100644 index 000000000..9f52e5b93 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/external_user_deletion_result_v2025_r0.py @@ -0,0 +1,23 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ExternalUserDeletionResultV2025R0(BaseObject): + def __init__( + self, user_id: str, status: int, *, detail: Optional[str] = None, **kwargs + ): + """ + :param user_id: The ID of the external user. + :type user_id: str + :param status: HTTP status code for a specific user's deletion request. + :type status: int + :param detail: Deletion request status details. This property is only present when the deletion request is not successful., defaults to None + :type detail: Optional[str], optional + """ + super().__init__(**kwargs) + self.user_id = user_id + self.status = status + self.detail = detail diff --git a/box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_request_v2025_r0.py new file mode 100644 index 000000000..29960b159 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_request_v2025_r0.py @@ -0,0 +1,17 @@ +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.user_reference_v2025_r0 import UserReferenceV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class ExternalUsersSubmitDeleteJobRequestV2025R0(BaseObject): + def __init__(self, external_users: List[UserReferenceV2025R0], **kwargs): + """ + :param external_users: List of external users to delete. + :type external_users: List[UserReferenceV2025R0] + """ + super().__init__(**kwargs) + self.external_users = external_users diff --git a/box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_response_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_response_v2025_r0.py new file mode 100644 index 000000000..9c75db4e9 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/external_users_submit_delete_job_response_v2025_r0.py @@ -0,0 +1,19 @@ +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.external_user_deletion_result_v2025_r0 import ( + ExternalUserDeletionResultV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ExternalUsersSubmitDeleteJobResponseV2025R0(BaseObject): + def __init__(self, entries: List[ExternalUserDeletionResultV2025R0], **kwargs): + """ + :param entries: Array of results of each external user deletion request. + :type entries: List[ExternalUserDeletionResultV2025R0] + """ + super().__init__(**kwargs) + self.entries = entries diff --git a/box_sdk_gen/schemas/v2025_r0/file_reference_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/file_reference_v2025_r0.py new file mode 100644 index 000000000..66216b28e --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/file_reference_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileReferenceV2025R0TypeField(str, Enum): + FILE = 'file' + + +class FileReferenceV2025R0(BaseObject): + _discriminator = 'type', {'file'} + + def __init__( + self, + id: str, + *, + type: FileReferenceV2025R0TypeField = FileReferenceV2025R0TypeField.FILE, + **kwargs + ): + """ + :param id: ID of the object. + :type id: str + :param type: The value will always be `file`., defaults to FileReferenceV2025R0TypeField.FILE + :type type: FileReferenceV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/file_version_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/file_version_base_v2025_r0.py new file mode 100644 index 000000000..fb4cd1842 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/file_version_base_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class FileVersionBaseV2025R0TypeField(str, Enum): + FILE_VERSION = 'file_version' + + +class FileVersionBaseV2025R0(BaseObject): + _discriminator = 'type', {'file_version'} + + def __init__( + self, + id: str, + *, + type: FileVersionBaseV2025R0TypeField = FileVersionBaseV2025R0TypeField.FILE_VERSION, + **kwargs + ): + """ + :param id: The unique identifier that represent a file version. + :type id: str + :param type: The value will always be `file_version`., defaults to FileVersionBaseV2025R0TypeField.FILE_VERSION + :type type: FileVersionBaseV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/folder_reference_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/folder_reference_v2025_r0.py new file mode 100644 index 000000000..c45fccac3 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/folder_reference_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class FolderReferenceV2025R0TypeField(str, Enum): + FOLDER = 'folder' + + +class FolderReferenceV2025R0(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + id: str, + *, + type: FolderReferenceV2025R0TypeField = FolderReferenceV2025R0TypeField.FOLDER, + **kwargs + ): + """ + :param id: ID of the folder. + :type id: str + :param type: The value will always be `folder`., defaults to FolderReferenceV2025R0TypeField.FOLDER + :type type: FolderReferenceV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/group_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/group_base_v2025_r0.py new file mode 100644 index 000000000..b14762bf1 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/group_base_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class GroupBaseV2025R0TypeField(str, Enum): + GROUP = 'group' + + +class GroupBaseV2025R0(BaseObject): + _discriminator = 'type', {'group'} + + def __init__( + self, + id: str, + *, + type: GroupBaseV2025R0TypeField = GroupBaseV2025R0TypeField.GROUP, + **kwargs + ): + """ + :param id: The unique identifier for this object. + :type id: str + :param type: The value will always be `group`., defaults to GroupBaseV2025R0TypeField.GROUP + :type type: GroupBaseV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/group_mini_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/group_mini_v2025_r0.py new file mode 100644 index 000000000..965e5e513 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/group_mini_v2025_r0.py @@ -0,0 +1,39 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.v2025_r0.group_base_v2025_r0 import GroupBaseV2025R0TypeField + +from box_sdk_gen.schemas.v2025_r0.group_base_v2025_r0 import GroupBaseV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class GroupMiniV2025R0GroupTypeField(str, Enum): + MANAGED_GROUP = 'managed_group' + ALL_USERS_GROUP = 'all_users_group' + + +class GroupMiniV2025R0(GroupBaseV2025R0): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + group_type: Optional[GroupMiniV2025R0GroupTypeField] = None, + type: GroupBaseV2025R0TypeField = GroupBaseV2025R0TypeField.GROUP, + **kwargs + ): + """ + :param id: The unique identifier for this object. + :type id: str + :param name: The name of the group., defaults to None + :type name: Optional[str], optional + :param group_type: The type of the group., defaults to None + :type group_type: Optional[GroupMiniV2025R0GroupTypeField], optional + :param type: The value will always be `group`., defaults to GroupBaseV2025R0TypeField.GROUP + :type type: GroupBaseV2025R0TypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name + self.group_type = group_type diff --git a/box_sdk_gen/schemas/v2025_r0/hub_access_grantee_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_access_grantee_v2025_r0.py new file mode 100644 index 000000000..ccabcea22 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_access_grantee_v2025_r0.py @@ -0,0 +1,11 @@ +from typing import Union + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_user_v2025_r0 import ( + HubCollaborationUserV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.group_mini_v2025_r0 import GroupMiniV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + +HubAccessGranteeV2025R0 = Union[HubCollaborationUserV2025R0, GroupMiniV2025R0] diff --git a/box_sdk_gen/schemas/v2025_r0/hub_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_base_v2025_r0.py new file mode 100644 index 000000000..099703dd6 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_base_v2025_r0.py @@ -0,0 +1,36 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubBaseV2025R0TypeField(str, Enum): + HUBS = 'hubs' + + +class HubBaseV2025R0(BaseObject): + _discriminator = 'type', {'hubs'} + + def __init__( + self, + id: str, + *, + type: HubBaseV2025R0TypeField = HubBaseV2025R0TypeField.HUBS, + **kwargs + ): + """ + :param id: The unique identifier that represent a Box Hub. + + The ID for any Box Hub can be determined + by visiting a Box Hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + :type id: str + :param type: The value will always be `hubs`., defaults to HubBaseV2025R0TypeField.HUBS + :type type: HubBaseV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/hub_collaboration_create_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_create_request_v2025_r0.py new file mode 100644 index 000000000..1fc7eb978 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_create_request_v2025_r0.py @@ -0,0 +1,84 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import Optional + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubCollaborationCreateRequestV2025R0HubTypeField(str, Enum): + HUBS = 'hubs' + + +class HubCollaborationCreateRequestV2025R0HubField(BaseObject): + _discriminator = 'type', {'hubs'} + + def __init__( + self, + id: str, + *, + type: HubCollaborationCreateRequestV2025R0HubTypeField = HubCollaborationCreateRequestV2025R0HubTypeField.HUBS, + **kwargs + ): + """ + :param id: ID of the object. + :type id: str + :param type: The value will always be `hubs`., defaults to HubCollaborationCreateRequestV2025R0HubTypeField.HUBS + :type type: HubCollaborationCreateRequestV2025R0HubTypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class HubCollaborationCreateRequestV2025R0AccessibleByField(BaseObject): + def __init__( + self, + type: str, + *, + id: Optional[str] = None, + login: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of collaborator to invite. + Possible values are `user` or `group`. + :type type: str + :param id: The ID of the user or group. + + Alternatively, use `login` to specify a user by email + address., defaults to None + :type id: Optional[str], optional + :param login: The email address of the user who gets access to the item. + + Alternatively, use `id` to specify a user by user ID., defaults to None + :type login: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + self.login = login + + +class HubCollaborationCreateRequestV2025R0(BaseObject): + def __init__( + self, + hub: HubCollaborationCreateRequestV2025R0HubField, + accessible_by: HubCollaborationCreateRequestV2025R0AccessibleByField, + role: str, + **kwargs + ): + """ + :param hub: Box Hubs reference. + :type hub: HubCollaborationCreateRequestV2025R0HubField + :param accessible_by: The user or group who gets access to the item. + :type accessible_by: HubCollaborationCreateRequestV2025R0AccessibleByField + :param role: The level of access granted to a Box Hub. + Possible values are `editor`, `viewer`, and `co-owner`. + :type role: str + """ + super().__init__(**kwargs) + self.hub = hub + self.accessible_by = accessible_by + self.role = role diff --git a/box_sdk_gen/schemas/v2025_r0/hub_collaboration_update_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_update_request_v2025_r0.py new file mode 100644 index 000000000..388c4049e --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_update_request_v2025_r0.py @@ -0,0 +1,16 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubCollaborationUpdateRequestV2025R0(BaseObject): + def __init__(self, *, role: Optional[str] = None, **kwargs): + """ + :param role: The level of access granted to a Box Hub. + Possible values are `editor`, `viewer`, and `co-owner`., defaults to None + :type role: Optional[str], optional + """ + super().__init__(**kwargs) + self.role = role diff --git a/box_sdk_gen/schemas/v2025_r0/hub_collaboration_user_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_user_v2025_r0.py new file mode 100644 index 000000000..cff4046ce --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_user_v2025_r0.py @@ -0,0 +1,32 @@ +from typing import Optional + +from box_sdk_gen.schemas.v2025_r0.user_base_v2025_r0 import UserBaseV2025R0TypeField + +from box_sdk_gen.schemas.v2025_r0.user_base_v2025_r0 import UserBaseV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubCollaborationUserV2025R0(UserBaseV2025R0): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + login: Optional[str] = None, + type: UserBaseV2025R0TypeField = UserBaseV2025R0TypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param name: The display name of this user. If the collaboration status is `pending`, an empty string is returned., defaults to None + :type name: Optional[str], optional + :param login: The primary email address of this user. If the collaboration status is `pending`, an empty string is returned., defaults to None + :type login: Optional[str], optional + :param type: The value will always be `user`., defaults to UserBaseV2025R0TypeField.USER + :type type: UserBaseV2025R0TypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name + self.login = login diff --git a/box_sdk_gen/schemas/v2025_r0/hub_collaboration_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_v2025_r0.py new file mode 100644 index 000000000..78411ef88 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_collaboration_v2025_r0.py @@ -0,0 +1,173 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_user_v2025_r0 import ( + HubCollaborationUserV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.group_mini_v2025_r0 import GroupMiniV2025R0 + +from box_sdk_gen.schemas.v2025_r0.hub_base_v2025_r0 import HubBaseV2025R0 + +from box_sdk_gen.schemas.v2025_r0.hub_access_grantee_v2025_r0 import ( + HubAccessGranteeV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.terms_of_service_base_v2025_r0 import ( + TermsOfServiceBaseV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubCollaborationV2025R0TypeField(str, Enum): + HUB_COLLABORATION = 'hub_collaboration' + + +class HubCollaborationV2025R0StatusField(str, Enum): + ACCEPTED = 'accepted' + PENDING = 'pending' + REJECTED = 'rejected' + + +class HubCollaborationV2025R0AcceptanceRequirementsStatusTermsOfServiceRequirementField( + BaseObject +): + def __init__( + self, + *, + is_accepted: Optional[bool] = None, + terms_of_service: Optional[TermsOfServiceBaseV2025R0] = None, + **kwargs + ): + """ + :param is_accepted: Whether or not the terms of service have been accepted. The + field is `null` when there is no terms of service required., defaults to None + :type is_accepted: Optional[bool], optional + """ + super().__init__(**kwargs) + self.is_accepted = is_accepted + self.terms_of_service = terms_of_service + + +class HubCollaborationV2025R0AcceptanceRequirementsStatusStrongPasswordRequirementField( + BaseObject +): + def __init__( + self, + *, + enterprise_has_strong_password_required_for_external_users: Optional[ + bool + ] = None, + user_has_strong_password: Optional[bool] = None, + **kwargs + ): + """ + :param enterprise_has_strong_password_required_for_external_users: Whether or not the enterprise that owns the content requires + a strong password to collaborate on the content, or enforces + an exposed password detection for the external collaborators., defaults to None + :type enterprise_has_strong_password_required_for_external_users: Optional[bool], optional + :param user_has_strong_password: Whether or not the user has a strong and not exposed password set + for their account. The field is `null` when a strong password is + not required., defaults to None + :type user_has_strong_password: Optional[bool], optional + """ + super().__init__(**kwargs) + self.enterprise_has_strong_password_required_for_external_users = ( + enterprise_has_strong_password_required_for_external_users + ) + self.user_has_strong_password = user_has_strong_password + + +class HubCollaborationV2025R0AcceptanceRequirementsStatusTwoFactorAuthenticationRequirementField( + BaseObject +): + def __init__( + self, + *, + enterprise_has_two_factor_auth_enabled: Optional[bool] = None, + user_has_two_factor_authentication_enabled: Optional[bool] = None, + **kwargs + ): + """ + :param enterprise_has_two_factor_auth_enabled: Whether or not the enterprise that owns the content requires + two-factor authentication to be enabled in order to + collaborate on the content., defaults to None + :type enterprise_has_two_factor_auth_enabled: Optional[bool], optional + :param user_has_two_factor_authentication_enabled: Whether or not the user has two-factor authentication + enabled. The field is `null` when two-factor + authentication is not required., defaults to None + :type user_has_two_factor_authentication_enabled: Optional[bool], optional + """ + super().__init__(**kwargs) + self.enterprise_has_two_factor_auth_enabled = ( + enterprise_has_two_factor_auth_enabled + ) + self.user_has_two_factor_authentication_enabled = ( + user_has_two_factor_authentication_enabled + ) + + +class HubCollaborationV2025R0AcceptanceRequirementsStatusField(BaseObject): + def __init__( + self, + *, + terms_of_service_requirement: Optional[ + HubCollaborationV2025R0AcceptanceRequirementsStatusTermsOfServiceRequirementField + ] = None, + strong_password_requirement: Optional[ + HubCollaborationV2025R0AcceptanceRequirementsStatusStrongPasswordRequirementField + ] = None, + two_factor_authentication_requirement: Optional[ + HubCollaborationV2025R0AcceptanceRequirementsStatusTwoFactorAuthenticationRequirementField + ] = None, + **kwargs + ): + super().__init__(**kwargs) + self.terms_of_service_requirement = terms_of_service_requirement + self.strong_password_requirement = strong_password_requirement + self.two_factor_authentication_requirement = ( + two_factor_authentication_requirement + ) + + +class HubCollaborationV2025R0(BaseObject): + _discriminator = 'type', {'hub_collaboration'} + + def __init__( + self, + id: str, + *, + type: HubCollaborationV2025R0TypeField = HubCollaborationV2025R0TypeField.HUB_COLLABORATION, + hub: Optional[HubBaseV2025R0] = None, + accessible_by: Optional[HubAccessGranteeV2025R0] = None, + role: Optional[str] = None, + status: Optional[HubCollaborationV2025R0StatusField] = None, + acceptance_requirements_status: Optional[ + HubCollaborationV2025R0AcceptanceRequirementsStatusField + ] = None, + **kwargs + ): + """ + :param id: The unique identifier for this collaboration. + :type id: str + :param type: The value will always be `hub_collaboration`., defaults to HubCollaborationV2025R0TypeField.HUB_COLLABORATION + :type type: HubCollaborationV2025R0TypeField, optional + :param role: The level of access granted to a Box Hub. + Possible values are `editor`, `viewer`, and `co-owner`., defaults to None + :type role: Optional[str], optional + :param status: The status of the collaboration invitation. If the status + is `pending`, `login` and `name` return an empty string., defaults to None + :type status: Optional[HubCollaborationV2025R0StatusField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.hub = hub + self.accessible_by = accessible_by + self.role = role + self.status = status + self.acceptance_requirements_status = acceptance_requirements_status diff --git a/box_sdk_gen/schemas/v2025_r0/hub_collaborations_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_collaborations_v2025_r0.py new file mode 100644 index 000000000..9a319ff00 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_collaborations_v2025_r0.py @@ -0,0 +1,36 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.hub_collaboration_v2025_r0 import ( + HubCollaborationV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubCollaborationsV2025R0(BaseObject): + def __init__( + self, + *, + entries: Optional[List[HubCollaborationV2025R0]] = None, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: A list of Box Hub collaborations., defaults to None + :type entries: Optional[List[HubCollaborationV2025R0]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/v2025_r0/hub_copy_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_copy_request_v2025_r0.py new file mode 100644 index 000000000..a3926a9a2 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_copy_request_v2025_r0.py @@ -0,0 +1,24 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubCopyRequestV2025R0(BaseObject): + def __init__( + self, + *, + title: Optional[str] = None, + description: Optional[str] = None, + **kwargs + ): + """ + :param title: Title of the Box Hub. It cannot be empty and should be less than 50 characters., defaults to None + :type title: Optional[str], optional + :param description: Description of the Box Hub., defaults to None + :type description: Optional[str], optional + """ + super().__init__(**kwargs) + self.title = title + self.description = description diff --git a/box_sdk_gen/schemas/v2025_r0/hub_create_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_create_request_v2025_r0.py new file mode 100644 index 000000000..f825bd932 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_create_request_v2025_r0.py @@ -0,0 +1,18 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubCreateRequestV2025R0(BaseObject): + def __init__(self, title: str, *, description: Optional[str] = None, **kwargs): + """ + :param title: Title of the Box Hub. It cannot be empty and should be less than 50 characters. + :type title: str + :param description: Description of the Box Hub., defaults to None + :type description: Optional[str], optional + """ + super().__init__(**kwargs) + self.title = title + self.description = description diff --git a/box_sdk_gen/schemas/v2025_r0/hub_item_operation_result_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_item_operation_result_v2025_r0.py new file mode 100644 index 000000000..f1423d2c2 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_item_operation_result_v2025_r0.py @@ -0,0 +1,44 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.folder_reference_v2025_r0 import ( + FolderReferenceV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.weblink_reference_v2025_r0 import ( + WeblinkReferenceV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_item_reference_v2025_r0 import ( + HubItemReferenceV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubItemOperationResultV2025R0(BaseObject): + def __init__( + self, + *, + action: Optional[str] = None, + item: Optional[HubItemReferenceV2025R0] = None, + status: Optional[int] = None, + error: Optional[str] = None, + **kwargs + ): + """ + :param action: The action performed on the item., defaults to None + :type action: Optional[str], optional + :param status: The HTTP status code of the operation., defaults to None + :type status: Optional[int], optional + :param error: Error message if the operation failed., defaults to None + :type error: Optional[str], optional + """ + super().__init__(**kwargs) + self.action = action + self.item = item + self.status = status + self.error = error diff --git a/box_sdk_gen/schemas/v2025_r0/hub_item_operation_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_item_operation_v2025_r0.py new file mode 100644 index 000000000..9c6f99759 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_item_operation_v2025_r0.py @@ -0,0 +1,40 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.folder_reference_v2025_r0 import ( + FolderReferenceV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.weblink_reference_v2025_r0 import ( + WeblinkReferenceV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.hub_item_reference_v2025_r0 import ( + HubItemReferenceV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubItemOperationV2025R0ActionField(str, Enum): + ADD = 'add' + REMOVE = 'remove' + + +class HubItemOperationV2025R0(BaseObject): + def __init__( + self, + action: HubItemOperationV2025R0ActionField, + item: HubItemReferenceV2025R0, + **kwargs + ): + """ + :param action: The action to perform on a Box Hub item. + :type action: HubItemOperationV2025R0ActionField + """ + super().__init__(**kwargs) + self.action = action + self.item = item diff --git a/box_sdk_gen/schemas/v2025_r0/hub_item_reference_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_item_reference_v2025_r0.py new file mode 100644 index 000000000..545cb7079 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_item_reference_v2025_r0.py @@ -0,0 +1,17 @@ +from typing import Union + +from box_sdk_gen.schemas.v2025_r0.file_reference_v2025_r0 import FileReferenceV2025R0 + +from box_sdk_gen.schemas.v2025_r0.folder_reference_v2025_r0 import ( + FolderReferenceV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.weblink_reference_v2025_r0 import ( + WeblinkReferenceV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +HubItemReferenceV2025R0 = Union[ + FileReferenceV2025R0, FolderReferenceV2025R0, WeblinkReferenceV2025R0 +] diff --git a/box_sdk_gen/schemas/v2025_r0/hub_item_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_item_v2025_r0.py new file mode 100644 index 000000000..f2fae55a6 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_item_v2025_r0.py @@ -0,0 +1,29 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubItemV2025R0TypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + WEB_LINK = 'web_link' + + +class HubItemV2025R0(BaseObject): + _discriminator = 'type', {'file', 'folder', 'web_link'} + + def __init__(self, id: str, type: HubItemV2025R0TypeField, name: str, **kwargs): + """ + :param id: The unique identifier for this item. + :type id: str + :param type: The type of the item. + :type type: HubItemV2025R0TypeField + :param name: The name of the item. + :type name: str + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name diff --git a/box_sdk_gen/schemas/v2025_r0/hub_items_manage_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_items_manage_request_v2025_r0.py new file mode 100644 index 000000000..17d0f3e8d --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_items_manage_request_v2025_r0.py @@ -0,0 +1,23 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.hub_item_operation_v2025_r0 import ( + HubItemOperationV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubItemsManageRequestV2025R0(BaseObject): + def __init__( + self, *, operations: Optional[List[HubItemOperationV2025R0]] = None, **kwargs + ): + """ + :param operations: List of operations to perform on Box Hub items., defaults to None + :type operations: Optional[List[HubItemOperationV2025R0]], optional + """ + super().__init__(**kwargs) + self.operations = operations diff --git a/box_sdk_gen/schemas/v2025_r0/hub_items_manage_response_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_items_manage_response_v2025_r0.py new file mode 100644 index 000000000..030b01ceb --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_items_manage_response_v2025_r0.py @@ -0,0 +1,19 @@ +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.hub_item_operation_result_v2025_r0 import ( + HubItemOperationResultV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubItemsManageResponseV2025R0(BaseObject): + def __init__(self, operations: List[HubItemOperationResultV2025R0], **kwargs): + """ + :param operations: List of operations performed on Box Hub items. + :type operations: List[HubItemOperationResultV2025R0] + """ + super().__init__(**kwargs) + self.operations = operations diff --git a/box_sdk_gen/schemas/v2025_r0/hub_items_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_items_v2025_r0.py new file mode 100644 index 000000000..6a792b86a --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_items_v2025_r0.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.hub_item_v2025_r0 import HubItemV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubItemsV2025R0(BaseObject): + def __init__( + self, + *, + entries: Optional[List[HubItemV2025R0]] = None, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: A list of Box Hub items., defaults to None + :type entries: Optional[List[HubItemV2025R0]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/v2025_r0/hub_update_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_update_request_v2025_r0.py new file mode 100644 index 000000000..67a29953d --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_update_request_v2025_r0.py @@ -0,0 +1,42 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubUpdateRequestV2025R0(BaseObject): + def __init__( + self, + *, + title: Optional[str] = None, + description: Optional[str] = None, + is_ai_enabled: Optional[bool] = None, + is_collaboration_restricted_to_enterprise: Optional[bool] = None, + can_non_owners_invite: Optional[bool] = None, + can_shared_link_be_created: Optional[bool] = None, + **kwargs + ): + """ + :param title: Title of the Box Hub. It cannot be empty and should be less than 50 characters., defaults to None + :type title: Optional[str], optional + :param description: Description of the Box Hub., defaults to None + :type description: Optional[str], optional + :param is_ai_enabled: Indicates if AI features are enabled for the Box Hub., defaults to None + :type is_ai_enabled: Optional[bool], optional + :param is_collaboration_restricted_to_enterprise: Indicates if collaboration is restricted to the enterprise., defaults to None + :type is_collaboration_restricted_to_enterprise: Optional[bool], optional + :param can_non_owners_invite: Indicates if non-owners can invite others to the Box Hub., defaults to None + :type can_non_owners_invite: Optional[bool], optional + :param can_shared_link_be_created: Indicates if a shared link can be created for the Box Hub., defaults to None + :type can_shared_link_be_created: Optional[bool], optional + """ + super().__init__(**kwargs) + self.title = title + self.description = description + self.is_ai_enabled = is_ai_enabled + self.is_collaboration_restricted_to_enterprise = ( + is_collaboration_restricted_to_enterprise + ) + self.can_non_owners_invite = can_non_owners_invite + self.can_shared_link_be_created = can_shared_link_be_created diff --git a/box_sdk_gen/schemas/v2025_r0/hub_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hub_v2025_r0.py new file mode 100644 index 000000000..51ed529aa --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hub_v2025_r0.py @@ -0,0 +1,78 @@ +from typing import Optional + +from box_sdk_gen.schemas.v2025_r0.hub_base_v2025_r0 import HubBaseV2025R0TypeField + +from box_sdk_gen.schemas.v2025_r0.hub_base_v2025_r0 import HubBaseV2025R0 + +from box_sdk_gen.schemas.v2025_r0.user_mini_v2025_r0 import UserMiniV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class HubV2025R0(HubBaseV2025R0): + def __init__( + self, + id: str, + *, + title: Optional[str] = None, + description: Optional[str] = None, + created_at: Optional[DateTime] = None, + updated_at: Optional[DateTime] = None, + created_by: Optional[UserMiniV2025R0] = None, + updated_by: Optional[UserMiniV2025R0] = None, + view_count: Optional[int] = None, + is_ai_enabled: Optional[bool] = None, + is_collaboration_restricted_to_enterprise: Optional[bool] = None, + can_non_owners_invite: Optional[bool] = None, + can_shared_link_be_created: Optional[bool] = None, + type: HubBaseV2025R0TypeField = HubBaseV2025R0TypeField.HUBS, + **kwargs + ): + """ + :param id: The unique identifier that represent a Box Hub. + + The ID for any Box Hub can be determined + by visiting a Box Hub in the web application + and copying the ID from the URL. For example, + for the URL `https://*.app.box.com/hubs/123` + the `hub_id` is `123`. + :type id: str + :param title: The title given to the Box Hub., defaults to None + :type title: Optional[str], optional + :param description: The description of the Box Hub. First 200 characters are returned., defaults to None + :type description: Optional[str], optional + :param created_at: The date and time when the folder was created. This value may + be `null` for some folders such as the root folder or the trash + folder., defaults to None + :type created_at: Optional[DateTime], optional + :param updated_at: The date and time when the Box Hub was last updated., defaults to None + :type updated_at: Optional[DateTime], optional + :param view_count: The number of views for the Box Hub., defaults to None + :type view_count: Optional[int], optional + :param is_ai_enabled: Indicates if AI features are enabled for the Box Hub., defaults to None + :type is_ai_enabled: Optional[bool], optional + :param is_collaboration_restricted_to_enterprise: Indicates if collaboration is restricted to the enterprise., defaults to None + :type is_collaboration_restricted_to_enterprise: Optional[bool], optional + :param can_non_owners_invite: Indicates if non-owners can invite others to the Box Hub., defaults to None + :type can_non_owners_invite: Optional[bool], optional + :param can_shared_link_be_created: Indicates if a shared link can be created for the Box Hub., defaults to None + :type can_shared_link_be_created: Optional[bool], optional + :param type: The value will always be `hubs`., defaults to HubBaseV2025R0TypeField.HUBS + :type type: HubBaseV2025R0TypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.title = title + self.description = description + self.created_at = created_at + self.updated_at = updated_at + self.created_by = created_by + self.updated_by = updated_by + self.view_count = view_count + self.is_ai_enabled = is_ai_enabled + self.is_collaboration_restricted_to_enterprise = ( + is_collaboration_restricted_to_enterprise + ) + self.can_non_owners_invite = can_non_owners_invite + self.can_shared_link_be_created = can_shared_link_be_created diff --git a/box_sdk_gen/schemas/v2025_r0/hubs_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/hubs_v2025_r0.py new file mode 100644 index 000000000..47c8cc9f1 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/hubs_v2025_r0.py @@ -0,0 +1,34 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.hub_v2025_r0 import HubV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class HubsV2025R0(BaseObject): + def __init__( + self, + *, + entries: Optional[List[HubV2025R0]] = None, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + **kwargs + ): + """ + :param entries: A list of hubs., defaults to None + :type entries: Optional[List[HubV2025R0]], optional + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + """ + super().__init__(**kwargs) + self.entries = entries + self.limit = limit + self.next_marker = next_marker diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_content_country_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_content_country_v2025_r0.py new file mode 100644 index 000000000..c1df40f1e --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_content_country_v2025_r0.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListContentCountryV2025R0TypeField(str, Enum): + COUNTRY = 'country' + + +class ShieldListContentCountryV2025R0(BaseObject): + _discriminator = 'type', {'country'} + + def __init__( + self, + country_codes: List[str], + *, + type: ShieldListContentCountryV2025R0TypeField = ShieldListContentCountryV2025R0TypeField.COUNTRY, + **kwargs + ): + """ + :param country_codes: List of country codes values. + :type country_codes: List[str] + :param type: The type of content in the shield list., defaults to ShieldListContentCountryV2025R0TypeField.COUNTRY + :type type: ShieldListContentCountryV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.country_codes = country_codes + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_content_domain_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_content_domain_v2025_r0.py new file mode 100644 index 000000000..6a0c0dae8 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_content_domain_v2025_r0.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListContentDomainV2025R0TypeField(str, Enum): + DOMAIN = 'domain' + + +class ShieldListContentDomainV2025R0(BaseObject): + _discriminator = 'type', {'domain'} + + def __init__( + self, + domains: List[str], + *, + type: ShieldListContentDomainV2025R0TypeField = ShieldListContentDomainV2025R0TypeField.DOMAIN, + **kwargs + ): + """ + :param domains: List of domain. + :type domains: List[str] + :param type: The type of content in the shield list., defaults to ShieldListContentDomainV2025R0TypeField.DOMAIN + :type type: ShieldListContentDomainV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.domains = domains + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_content_email_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_content_email_v2025_r0.py new file mode 100644 index 000000000..6883277bd --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_content_email_v2025_r0.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListContentEmailV2025R0TypeField(str, Enum): + EMAIL = 'email' + + +class ShieldListContentEmailV2025R0(BaseObject): + _discriminator = 'type', {'email'} + + def __init__( + self, + email_addresses: List[str], + *, + type: ShieldListContentEmailV2025R0TypeField = ShieldListContentEmailV2025R0TypeField.EMAIL, + **kwargs + ): + """ + :param email_addresses: List of emails. + :type email_addresses: List[str] + :param type: The type of content in the shield list., defaults to ShieldListContentEmailV2025R0TypeField.EMAIL + :type type: ShieldListContentEmailV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.email_addresses = email_addresses + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_content_integration_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_content_integration_v2025_r0.py new file mode 100644 index 000000000..a1a9f327b --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_content_integration_v2025_r0.py @@ -0,0 +1,44 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListContentIntegrationV2025R0TypeField(str, Enum): + INTEGRATION = 'integration' + + +class ShieldListContentIntegrationV2025R0IntegrationsField(BaseObject): + def __init__(self, *, id: Optional[str] = None, **kwargs): + """ + :param id: The ID of the integration., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + + +class ShieldListContentIntegrationV2025R0(BaseObject): + _discriminator = 'type', {'integration'} + + def __init__( + self, + integrations: List[ShieldListContentIntegrationV2025R0IntegrationsField], + *, + type: ShieldListContentIntegrationV2025R0TypeField = ShieldListContentIntegrationV2025R0TypeField.INTEGRATION, + **kwargs + ): + """ + :param integrations: List of integration. + :type integrations: List[ShieldListContentIntegrationV2025R0IntegrationsField] + :param type: The type of content in the shield list., defaults to ShieldListContentIntegrationV2025R0TypeField.INTEGRATION + :type type: ShieldListContentIntegrationV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.integrations = integrations + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_content_ip_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_content_ip_v2025_r0.py new file mode 100644 index 000000000..74fd61531 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_content_ip_v2025_r0.py @@ -0,0 +1,32 @@ +from enum import Enum + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListContentIpV2025R0TypeField(str, Enum): + IP = 'ip' + + +class ShieldListContentIpV2025R0(BaseObject): + _discriminator = 'type', {'ip'} + + def __init__( + self, + ip_addresses: List[str], + *, + type: ShieldListContentIpV2025R0TypeField = ShieldListContentIpV2025R0TypeField.IP, + **kwargs + ): + """ + :param ip_addresses: List of ip addresses and CIDRs. + :type ip_addresses: List[str] + :param type: The type of content in the shield list., defaults to ShieldListContentIpV2025R0TypeField.IP + :type type: ShieldListContentIpV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.ip_addresses = ip_addresses + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_content_request_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_content_request_v2025_r0.py new file mode 100644 index 000000000..266a96244 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_content_request_v2025_r0.py @@ -0,0 +1,26 @@ +from typing import Union + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_country_v2025_r0 import ( + ShieldListContentCountryV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_domain_v2025_r0 import ( + ShieldListContentDomainV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_email_v2025_r0 import ( + ShieldListContentEmailV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_ip_v2025_r0 import ( + ShieldListContentIpV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +ShieldListContentRequestV2025R0 = Union[ + ShieldListContentCountryV2025R0, + ShieldListContentDomainV2025R0, + ShieldListContentEmailV2025R0, + ShieldListContentIpV2025R0, +] diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_content_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_content_v2025_r0.py new file mode 100644 index 000000000..9c5adacf8 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_content_v2025_r0.py @@ -0,0 +1,31 @@ +from typing import Union + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_country_v2025_r0 import ( + ShieldListContentCountryV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_domain_v2025_r0 import ( + ShieldListContentDomainV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_email_v2025_r0 import ( + ShieldListContentEmailV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_ip_v2025_r0 import ( + ShieldListContentIpV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_integration_v2025_r0 import ( + ShieldListContentIntegrationV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +ShieldListContentV2025R0 = Union[ + ShieldListContentCountryV2025R0, + ShieldListContentDomainV2025R0, + ShieldListContentEmailV2025R0, + ShieldListContentIpV2025R0, + ShieldListContentIntegrationV2025R0, +] diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_mini_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_mini_v2025_r0.py new file mode 100644 index 000000000..1bd5f412d --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_mini_v2025_r0.py @@ -0,0 +1,50 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListMiniV2025R0TypeField(str, Enum): + SHIELD_LIST = 'shield_list' + + +class ShieldListMiniV2025R0ContentField(BaseObject): + def __init__(self, *, type: Optional[str] = None, **kwargs): + """ + :param type: The type of content in the shield list., defaults to None + :type type: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + + +class ShieldListMiniV2025R0(BaseObject): + _discriminator = 'type', {'shield_list'} + + def __init__( + self, + id: str, + name: str, + content: ShieldListMiniV2025R0ContentField, + *, + type: ShieldListMiniV2025R0TypeField = ShieldListMiniV2025R0TypeField.SHIELD_LIST, + **kwargs + ): + """ + :param id: Unique global identifier for this list. + :type id: str + :param name: Name of Shield List. + :type name: str + :param type: The type of object., defaults to ShieldListMiniV2025R0TypeField.SHIELD_LIST + :type type: ShieldListMiniV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.name = name + self.content = content + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/shield_list_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_list_v2025_r0.py new file mode 100644 index 000000000..a7981b729 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_list_v2025_r0.py @@ -0,0 +1,76 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_country_v2025_r0 import ( + ShieldListContentCountryV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_domain_v2025_r0 import ( + ShieldListContentDomainV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_email_v2025_r0 import ( + ShieldListContentEmailV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_ip_v2025_r0 import ( + ShieldListContentIpV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_integration_v2025_r0 import ( + ShieldListContentIntegrationV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.enterprise_reference_v2025_r0 import ( + EnterpriseReferenceV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_v2025_r0 import ( + ShieldListContentV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class ShieldListV2025R0(BaseObject): + def __init__( + self, + id: str, + type: str, + name: str, + enterprise: EnterpriseReferenceV2025R0, + created_at: DateTime, + updated_at: DateTime, + content: ShieldListContentV2025R0, + *, + description: Optional[str] = None, + **kwargs + ): + """ + :param id: Unique identifier for the shield list. + :type id: str + :param type: Type of the object. + :type type: str + :param name: Name of the shield list. + :type name: str + :param created_at: ISO date time string when this shield list object was created. + :type created_at: DateTime + :param updated_at: ISO date time string when this shield list object was updated. + :type updated_at: DateTime + :param description: Description of Shield List., defaults to None + :type description: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + self.enterprise = enterprise + self.created_at = created_at + self.updated_at = updated_at + self.content = content + self.description = description diff --git a/box_sdk_gen/schemas/v2025_r0/shield_lists_create_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_lists_create_v2025_r0.py new file mode 100644 index 000000000..c582f4c8e --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_lists_create_v2025_r0.py @@ -0,0 +1,48 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_country_v2025_r0 import ( + ShieldListContentCountryV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_domain_v2025_r0 import ( + ShieldListContentDomainV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_email_v2025_r0 import ( + ShieldListContentEmailV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_ip_v2025_r0 import ( + ShieldListContentIpV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_request_v2025_r0 import ( + ShieldListContentRequestV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListsCreateV2025R0(BaseObject): + def __init__( + self, + name: str, + content: ShieldListContentRequestV2025R0, + *, + description: Optional[str] = None, + **kwargs + ): + """ + :param name: The name of the shield list. + :type name: str + :param description: Optional description of Shield List., defaults to None + :type description: Optional[str], optional + """ + super().__init__(**kwargs) + self.name = name + self.content = content + self.description = description diff --git a/box_sdk_gen/schemas/v2025_r0/shield_lists_update_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_lists_update_v2025_r0.py new file mode 100644 index 000000000..6a85584a9 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_lists_update_v2025_r0.py @@ -0,0 +1,48 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_country_v2025_r0 import ( + ShieldListContentCountryV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_domain_v2025_r0 import ( + ShieldListContentDomainV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_email_v2025_r0 import ( + ShieldListContentEmailV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_ip_v2025_r0 import ( + ShieldListContentIpV2025R0, +) + +from box_sdk_gen.schemas.v2025_r0.shield_list_content_request_v2025_r0 import ( + ShieldListContentRequestV2025R0, +) + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListsUpdateV2025R0(BaseObject): + def __init__( + self, + name: str, + content: ShieldListContentRequestV2025R0, + *, + description: Optional[str] = None, + **kwargs + ): + """ + :param name: The name of the shield list. + :type name: str + :param description: Optional description of Shield List., defaults to None + :type description: Optional[str], optional + """ + super().__init__(**kwargs) + self.name = name + self.content = content + self.description = description diff --git a/box_sdk_gen/schemas/v2025_r0/shield_lists_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/shield_lists_v2025_r0.py new file mode 100644 index 000000000..1b2beceeb --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/shield_lists_v2025_r0.py @@ -0,0 +1,21 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.v2025_r0.shield_list_mini_v2025_r0 import ShieldListMiniV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class ShieldListsV2025R0(BaseObject): + def __init__( + self, *, entries: Optional[List[ShieldListMiniV2025R0]] = None, **kwargs + ): + """ + :param entries: A list of shield list objects., defaults to None + :type entries: Optional[List[ShieldListMiniV2025R0]], optional + """ + super().__init__(**kwargs) + self.entries = entries diff --git a/box_sdk_gen/schemas/v2025_r0/terms_of_service_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/terms_of_service_base_v2025_r0.py new file mode 100644 index 000000000..0d7c7ae0b --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/terms_of_service_base_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class TermsOfServiceBaseV2025R0TypeField(str, Enum): + TERMS_OF_SERVICE = 'terms_of_service' + + +class TermsOfServiceBaseV2025R0(BaseObject): + _discriminator = 'type', {'terms_of_service'} + + def __init__( + self, + id: str, + *, + type: TermsOfServiceBaseV2025R0TypeField = TermsOfServiceBaseV2025R0TypeField.TERMS_OF_SERVICE, + **kwargs + ): + """ + :param id: The unique identifier for this terms of service. + :type id: str + :param type: The value will always be `terms_of_service`., defaults to TermsOfServiceBaseV2025R0TypeField.TERMS_OF_SERVICE + :type type: TermsOfServiceBaseV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/user_base_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/user_base_v2025_r0.py new file mode 100644 index 000000000..f5910d63f --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/user_base_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserBaseV2025R0TypeField(str, Enum): + USER = 'user' + + +class UserBaseV2025R0(BaseObject): + _discriminator = 'type', {'user'} + + def __init__( + self, + id: str, + *, + type: UserBaseV2025R0TypeField = UserBaseV2025R0TypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param type: The value will always be `user`., defaults to UserBaseV2025R0TypeField.USER + :type type: UserBaseV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/user_mini_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/user_mini_v2025_r0.py new file mode 100644 index 000000000..f9ef21883 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/user_mini_v2025_r0.py @@ -0,0 +1,32 @@ +from typing import Optional + +from box_sdk_gen.schemas.v2025_r0.user_base_v2025_r0 import UserBaseV2025R0TypeField + +from box_sdk_gen.schemas.v2025_r0.user_base_v2025_r0 import UserBaseV2025R0 + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserMiniV2025R0(UserBaseV2025R0): + def __init__( + self, + id: str, + *, + name: Optional[str] = None, + login: Optional[str] = None, + type: UserBaseV2025R0TypeField = UserBaseV2025R0TypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for this user. + :type id: str + :param name: The display name of this user., defaults to None + :type name: Optional[str], optional + :param login: The primary email address of this user., defaults to None + :type login: Optional[str], optional + :param type: The value will always be `user`., defaults to UserBaseV2025R0TypeField.USER + :type type: UserBaseV2025R0TypeField, optional + """ + super().__init__(id=id, type=type, **kwargs) + self.name = name + self.login = login diff --git a/box_sdk_gen/schemas/v2025_r0/user_reference_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/user_reference_v2025_r0.py new file mode 100644 index 000000000..afe32b0f5 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/user_reference_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class UserReferenceV2025R0TypeField(str, Enum): + USER = 'user' + + +class UserReferenceV2025R0(BaseObject): + _discriminator = 'type', {'user'} + + def __init__( + self, + id: str, + *, + type: UserReferenceV2025R0TypeField = UserReferenceV2025R0TypeField.USER, + **kwargs + ): + """ + :param id: The unique identifier for the user. + :type id: str + :param type: The value is always `user`., defaults to UserReferenceV2025R0TypeField.USER + :type type: UserReferenceV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/v2025_r0/weblink_reference_v2025_r0.py b/box_sdk_gen/schemas/v2025_r0/weblink_reference_v2025_r0.py new file mode 100644 index 000000000..d172f86e2 --- /dev/null +++ b/box_sdk_gen/schemas/v2025_r0/weblink_reference_v2025_r0.py @@ -0,0 +1,30 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class WeblinkReferenceV2025R0TypeField(str, Enum): + WEBLINK = 'weblink' + + +class WeblinkReferenceV2025R0(BaseObject): + _discriminator = 'type', {'weblink'} + + def __init__( + self, + id: str, + *, + type: WeblinkReferenceV2025R0TypeField = WeblinkReferenceV2025R0TypeField.WEBLINK, + **kwargs + ): + """ + :param id: ID of the web link. + :type id: str + :param type: The value will always be `weblink`., defaults to WeblinkReferenceV2025R0TypeField.WEBLINK + :type type: WeblinkReferenceV2025R0TypeField, optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type diff --git a/box_sdk_gen/schemas/watermark.py b/box_sdk_gen/schemas/watermark.py new file mode 100644 index 000000000..eb5464cdd --- /dev/null +++ b/box_sdk_gen/schemas/watermark.py @@ -0,0 +1,34 @@ +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class WatermarkWatermarkField(BaseObject): + def __init__( + self, + *, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + **kwargs + ): + """ + :param created_at: When this watermark was created., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When this task was modified., defaults to None + :type modified_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.created_at = created_at + self.modified_at = modified_at + + +class Watermark(BaseObject): + def __init__( + self, *, watermark: Optional[WatermarkWatermarkField] = None, **kwargs + ): + super().__init__(**kwargs) + self.watermark = watermark diff --git a/box_sdk_gen/schemas/web_link.py b/box_sdk_gen/schemas/web_link.py new file mode 100644 index 000000000..c85512f47 --- /dev/null +++ b/box_sdk_gen/schemas/web_link.py @@ -0,0 +1,248 @@ +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from enum import Enum + +from typing import Optional + +from box_sdk_gen.schemas.web_link_base import WebLinkBaseTypeField + +from box_sdk_gen.schemas.web_link_base import WebLinkBase + +from box_sdk_gen.schemas.web_link_mini import WebLinkMini + +from box_sdk_gen.schemas.folder_mini import FolderMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class WebLinkPathCollectionField(BaseObject): + def __init__(self, total_count: int, entries: List[FolderMini], **kwargs): + """ + :param total_count: The number of folders in this list. + :type total_count: int + :param entries: The parent folders for this item. + :type entries: List[FolderMini] + """ + super().__init__(**kwargs) + self.total_count = total_count + self.entries = entries + + +class WebLinkSharedLinkAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class WebLinkSharedLinkEffectiveAccessField(str, Enum): + OPEN = 'open' + COMPANY = 'company' + COLLABORATORS = 'collaborators' + + +class WebLinkSharedLinkEffectivePermissionField(str, Enum): + CAN_EDIT = 'can_edit' + CAN_DOWNLOAD = 'can_download' + CAN_PREVIEW = 'can_preview' + NO_ACCESS = 'no_access' + + +class WebLinkSharedLinkPermissionsField(BaseObject): + def __init__(self, can_download: bool, can_preview: bool, can_edit: bool, **kwargs): + """ + :param can_download: Defines if the shared link allows for the item to be downloaded. For + shared links on folders, this also applies to any items in the folder. + + This value can be set to `true` when the effective access level is + set to `open` or `company`, not `collaborators`. + :type can_download: bool + :param can_preview: Defines if the shared link allows for the item to be previewed. + + This value is always `true`. For shared links on folders this also + applies to any items in the folder. + :type can_preview: bool + :param can_edit: Defines if the shared link allows for the item to be edited. + + This value can only be `true` if `can_download` is also `true` and if + the item has a type of `file`. + :type can_edit: bool + """ + super().__init__(**kwargs) + self.can_download = can_download + self.can_preview = can_preview + self.can_edit = can_edit + + +class WebLinkSharedLinkField(BaseObject): + def __init__( + self, + url: str, + effective_access: WebLinkSharedLinkEffectiveAccessField, + effective_permission: WebLinkSharedLinkEffectivePermissionField, + is_password_enabled: bool, + download_count: int, + preview_count: int, + *, + download_url: Optional[str] = None, + vanity_url: Optional[str] = None, + vanity_name: Optional[str] = None, + access: Optional[WebLinkSharedLinkAccessField] = None, + unshared_at: Optional[DateTime] = None, + permissions: Optional[WebLinkSharedLinkPermissionsField] = None, + **kwargs + ): + """ + :param url: The URL that can be used to access the item on Box. + + This URL will display the item in Box's preview UI where the file + can be downloaded if allowed. + + This URL will continue to work even when a custom `vanity_url` + has been set for this shared link. + :type url: str + :param effective_access: The effective access level for the shared link. This can be a more + restrictive access level than the value in the `access` field when the + enterprise settings restrict the allowed access levels. + :type effective_access: WebLinkSharedLinkEffectiveAccessField + :param effective_permission: The effective permissions for this shared link. + These result in the more restrictive combination of + the share link permissions and the item permissions set + by the administrator, the owner, and any ancestor item + such as a folder. + :type effective_permission: WebLinkSharedLinkEffectivePermissionField + :param is_password_enabled: Defines if the shared link requires a password to access the item. + :type is_password_enabled: bool + :param download_count: The number of times this item has been downloaded. + :type download_count: int + :param preview_count: The number of times this item has been previewed. + :type preview_count: int + :param download_url: A URL that can be used to download the file. This URL can be used in + a browser to download the file. This URL includes the file + extension so that the file will be saved with the right file type. + + This property will be `null` for folders., defaults to None + :type download_url: Optional[str], optional + :param vanity_url: The "Custom URL" that can also be used to preview the item on Box. Custom + URLs can only be created or modified in the Box Web application., defaults to None + :type vanity_url: Optional[str], optional + :param vanity_name: The custom name of a shared link, as used in the `vanity_url` field., defaults to None + :type vanity_name: Optional[str], optional + :param access: The access level for this shared link. + + * `open` - provides access to this item to anyone with this link + * `company` - only provides access to this item to people the same company + * `collaborators` - only provides access to this item to people who are + collaborators on this item + + If this field is omitted when creating the shared link, the access level + will be set to the default access level specified by the enterprise admin., defaults to None + :type access: Optional[WebLinkSharedLinkAccessField], optional + :param unshared_at: The date and time when this link will be unshared. This field can only be + set by users with paid accounts., defaults to None + :type unshared_at: Optional[DateTime], optional + :param permissions: Defines if this link allows a user to preview, edit, and download an item. + These permissions refer to the shared link only and + do not supersede permissions applied to the item itself., defaults to None + :type permissions: Optional[WebLinkSharedLinkPermissionsField], optional + """ + super().__init__(**kwargs) + self.url = url + self.effective_access = effective_access + self.effective_permission = effective_permission + self.is_password_enabled = is_password_enabled + self.download_count = download_count + self.preview_count = preview_count + self.download_url = download_url + self.vanity_url = vanity_url + self.vanity_name = vanity_name + self.access = access + self.unshared_at = unshared_at + self.permissions = permissions + + +class WebLinkItemStatusField(str, Enum): + ACTIVE = 'active' + TRASHED = 'trashed' + DELETED = 'deleted' + + +class WebLink(WebLinkMini): + def __init__( + self, + id: str, + *, + parent: Optional[FolderMini] = None, + description: Optional[str] = None, + path_collection: Optional[WebLinkPathCollectionField] = None, + created_at: Optional[DateTime] = None, + modified_at: Optional[DateTime] = None, + trashed_at: Optional[DateTime] = None, + purged_at: Optional[DateTime] = None, + created_by: Optional[UserMini] = None, + modified_by: Optional[UserMini] = None, + owned_by: Optional[UserMini] = None, + shared_link: Optional[WebLinkSharedLinkField] = None, + item_status: Optional[WebLinkItemStatusField] = None, + url: Optional[str] = None, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + type: WebLinkBaseTypeField = WebLinkBaseTypeField.WEB_LINK, + etag: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this web link. + :type id: str + :param description: The description accompanying the web link. This is + visible within the Box web application., defaults to None + :type description: Optional[str], optional + :param created_at: When this file was created on Box’s servers., defaults to None + :type created_at: Optional[DateTime], optional + :param modified_at: When this file was last updated on the Box + servers., defaults to None + :type modified_at: Optional[DateTime], optional + :param trashed_at: When this file was moved to the trash., defaults to None + :type trashed_at: Optional[DateTime], optional + :param purged_at: When this file will be permanently deleted., defaults to None + :type purged_at: Optional[DateTime], optional + :param item_status: Whether this item is deleted or not. Values include `active`, + `trashed` if the file has been moved to the trash, and `deleted` if + the file has been permanently deleted., defaults to None + :type item_status: Optional[WebLinkItemStatusField], optional + :param url: The URL this web link points to., defaults to None + :type url: Optional[str], optional + :param name: The name of the web link., defaults to None + :type name: Optional[str], optional + :param type: The value will always be `web_link`., defaults to WebLinkBaseTypeField.WEB_LINK + :type type: WebLinkBaseTypeField, optional + :param etag: The entity tag of this web link. Used with `If-Match` + headers., defaults to None + :type etag: Optional[str], optional + """ + super().__init__( + id=id, + url=url, + sequence_id=sequence_id, + name=name, + type=type, + etag=etag, + **kwargs + ) + self.parent = parent + self.description = description + self.path_collection = path_collection + self.created_at = created_at + self.modified_at = modified_at + self.trashed_at = trashed_at + self.purged_at = purged_at + self.created_by = created_by + self.modified_by = modified_by + self.owned_by = owned_by + self.shared_link = shared_link + self.item_status = item_status diff --git a/box_sdk_gen/schemas/web_link_base.py b/box_sdk_gen/schemas/web_link_base.py new file mode 100644 index 000000000..5047cf051 --- /dev/null +++ b/box_sdk_gen/schemas/web_link_base.py @@ -0,0 +1,37 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class WebLinkBaseTypeField(str, Enum): + WEB_LINK = 'web_link' + + +class WebLinkBase(BaseObject): + _discriminator = 'type', {'web_link'} + + def __init__( + self, + id: str, + *, + type: WebLinkBaseTypeField = WebLinkBaseTypeField.WEB_LINK, + etag: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this web link. + :type id: str + :param type: The value will always be `web_link`., defaults to WebLinkBaseTypeField.WEB_LINK + :type type: WebLinkBaseTypeField, optional + :param etag: The entity tag of this web link. Used with `If-Match` + headers., defaults to None + :type etag: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.etag = etag diff --git a/box_sdk_gen/schemas/web_link_mini.py b/box_sdk_gen/schemas/web_link_mini.py new file mode 100644 index 000000000..8beb65852 --- /dev/null +++ b/box_sdk_gen/schemas/web_link_mini.py @@ -0,0 +1,38 @@ +from typing import Optional + +from box_sdk_gen.schemas.web_link_base import WebLinkBaseTypeField + +from box_sdk_gen.schemas.web_link_base import WebLinkBase + +from box_sdk_gen.box.errors import BoxSDKError + + +class WebLinkMini(WebLinkBase): + def __init__( + self, + id: str, + *, + url: Optional[str] = None, + sequence_id: Optional[str] = None, + name: Optional[str] = None, + type: WebLinkBaseTypeField = WebLinkBaseTypeField.WEB_LINK, + etag: Optional[str] = None, + **kwargs + ): + """ + :param id: The unique identifier for this web link. + :type id: str + :param url: The URL this web link points to., defaults to None + :type url: Optional[str], optional + :param name: The name of the web link., defaults to None + :type name: Optional[str], optional + :param type: The value will always be `web_link`., defaults to WebLinkBaseTypeField.WEB_LINK + :type type: WebLinkBaseTypeField, optional + :param etag: The entity tag of this web link. Used with `If-Match` + headers., defaults to None + :type etag: Optional[str], optional + """ + super().__init__(id=id, type=type, etag=etag, **kwargs) + self.url = url + self.sequence_id = sequence_id + self.name = name diff --git a/box_sdk_gen/schemas/webhook.py b/box_sdk_gen/schemas/webhook.py new file mode 100644 index 000000000..8163c7ee9 --- /dev/null +++ b/box_sdk_gen/schemas/webhook.py @@ -0,0 +1,99 @@ +from enum import Enum + +from typing import Optional + +from typing import List + +from box_sdk_gen.schemas.webhook_mini import WebhookMiniTypeField + +from box_sdk_gen.schemas.webhook_mini import WebhookMiniTargetField + +from box_sdk_gen.schemas.webhook_mini import WebhookMini + +from box_sdk_gen.schemas.user_mini import UserMini + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class WebhookTriggersField(str, Enum): + FILE_UPLOADED = 'FILE.UPLOADED' + FILE_PREVIEWED = 'FILE.PREVIEWED' + FILE_DOWNLOADED = 'FILE.DOWNLOADED' + FILE_TRASHED = 'FILE.TRASHED' + FILE_DELETED = 'FILE.DELETED' + FILE_RESTORED = 'FILE.RESTORED' + FILE_COPIED = 'FILE.COPIED' + FILE_MOVED = 'FILE.MOVED' + FILE_LOCKED = 'FILE.LOCKED' + FILE_UNLOCKED = 'FILE.UNLOCKED' + FILE_RENAMED = 'FILE.RENAMED' + COMMENT_CREATED = 'COMMENT.CREATED' + COMMENT_UPDATED = 'COMMENT.UPDATED' + COMMENT_DELETED = 'COMMENT.DELETED' + TASK_ASSIGNMENT_CREATED = 'TASK_ASSIGNMENT.CREATED' + TASK_ASSIGNMENT_UPDATED = 'TASK_ASSIGNMENT.UPDATED' + METADATA_INSTANCE_CREATED = 'METADATA_INSTANCE.CREATED' + METADATA_INSTANCE_UPDATED = 'METADATA_INSTANCE.UPDATED' + METADATA_INSTANCE_DELETED = 'METADATA_INSTANCE.DELETED' + FOLDER_CREATED = 'FOLDER.CREATED' + FOLDER_RENAMED = 'FOLDER.RENAMED' + FOLDER_DOWNLOADED = 'FOLDER.DOWNLOADED' + FOLDER_RESTORED = 'FOLDER.RESTORED' + FOLDER_DELETED = 'FOLDER.DELETED' + FOLDER_COPIED = 'FOLDER.COPIED' + FOLDER_MOVED = 'FOLDER.MOVED' + FOLDER_TRASHED = 'FOLDER.TRASHED' + WEBHOOK_DELETED = 'WEBHOOK.DELETED' + COLLABORATION_CREATED = 'COLLABORATION.CREATED' + COLLABORATION_ACCEPTED = 'COLLABORATION.ACCEPTED' + COLLABORATION_REJECTED = 'COLLABORATION.REJECTED' + COLLABORATION_REMOVED = 'COLLABORATION.REMOVED' + COLLABORATION_UPDATED = 'COLLABORATION.UPDATED' + SHARED_LINK_DELETED = 'SHARED_LINK.DELETED' + SHARED_LINK_CREATED = 'SHARED_LINK.CREATED' + SHARED_LINK_UPDATED = 'SHARED_LINK.UPDATED' + SIGN_REQUEST_COMPLETED = 'SIGN_REQUEST.COMPLETED' + SIGN_REQUEST_DECLINED = 'SIGN_REQUEST.DECLINED' + SIGN_REQUEST_EXPIRED = 'SIGN_REQUEST.EXPIRED' + SIGN_REQUEST_SIGNER_EMAIL_BOUNCED = 'SIGN_REQUEST.SIGNER_EMAIL_BOUNCED' + SIGN_REQUEST_SIGN_SIGNER_SIGNED = 'SIGN_REQUEST.SIGN_SIGNER_SIGNED' + SIGN_REQUEST_SIGN_DOCUMENT_CREATED = 'SIGN_REQUEST.SIGN_DOCUMENT_CREATED' + SIGN_REQUEST_SIGN_ERROR_FINALIZING = 'SIGN_REQUEST.SIGN_ERROR_FINALIZING' + + +class Webhook(WebhookMini): + def __init__( + self, + *, + created_by: Optional[UserMini] = None, + created_at: Optional[DateTime] = None, + address: Optional[str] = None, + triggers: Optional[List[WebhookTriggersField]] = None, + id: Optional[str] = None, + type: Optional[WebhookMiniTypeField] = None, + target: Optional[WebhookMiniTargetField] = None, + **kwargs + ): + """ + :param created_at: A timestamp identifying the time that + the webhook was created., defaults to None + :type created_at: Optional[DateTime], optional + :param address: The URL that is notified by this webhook., defaults to None + :type address: Optional[str], optional + :param triggers: An array of event names that this webhook is + to be triggered for., defaults to None + :type triggers: Optional[List[WebhookTriggersField]], optional + :param id: The unique identifier for this webhook., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `webhook`., defaults to None + :type type: Optional[WebhookMiniTypeField], optional + :param target: The item that will trigger the webhook., defaults to None + :type target: Optional[WebhookMiniTargetField], optional + """ + super().__init__(id=id, type=type, target=target, **kwargs) + self.created_by = created_by + self.created_at = created_at + self.address = address + self.triggers = triggers diff --git a/box_sdk_gen/schemas/webhook_mini.py b/box_sdk_gen/schemas/webhook_mini.py new file mode 100644 index 000000000..b9d33c3d8 --- /dev/null +++ b/box_sdk_gen/schemas/webhook_mini.py @@ -0,0 +1,62 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class WebhookMiniTypeField(str, Enum): + WEBHOOK = 'webhook' + + +class WebhookMiniTargetTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class WebhookMiniTargetField(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[WebhookMiniTargetTypeField] = None, + **kwargs + ): + """ + :param id: The ID of the item to trigger a webhook., defaults to None + :type id: Optional[str], optional + :param type: The type of item to trigger a webhook., defaults to None + :type type: Optional[WebhookMiniTargetTypeField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + + +class WebhookMini(BaseObject): + _discriminator = 'type', {'webhook'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[WebhookMiniTypeField] = None, + target: Optional[WebhookMiniTargetField] = None, + **kwargs + ): + """ + :param id: The unique identifier for this webhook., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `webhook`., defaults to None + :type type: Optional[WebhookMiniTypeField], optional + :param target: The item that will trigger the webhook., defaults to None + :type target: Optional[WebhookMiniTargetField], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.target = target diff --git a/box_sdk_gen/schemas/webhooks.py b/box_sdk_gen/schemas/webhooks.py new file mode 100644 index 000000000..779086e51 --- /dev/null +++ b/box_sdk_gen/schemas/webhooks.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.webhook_mini import WebhookMini + +from box_sdk_gen.box.errors import BoxSDKError + + +class Webhooks(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[WebhookMini]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of webhooks., defaults to None + :type entries: Optional[List[WebhookMini]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/workflow.py b/box_sdk_gen/schemas/workflow.py new file mode 100644 index 000000000..2d5ecc394 --- /dev/null +++ b/box_sdk_gen/schemas/workflow.py @@ -0,0 +1,295 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.schemas.workflow_mini import WorkflowMiniTypeField + +from box_sdk_gen.schemas.workflow_mini import WorkflowMini + +from box_sdk_gen.schemas.user_base import UserBase + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class WorkflowFlowsTypeField(str, Enum): + FLOW = 'flow' + + +class WorkflowFlowsTriggerTypeField(str, Enum): + TRIGGER = 'trigger' + + +class WorkflowFlowsTriggerTriggerTypeField(str, Enum): + WORKFLOW_MANUAL_START = 'WORKFLOW_MANUAL_START' + + +class WorkflowFlowsTriggerScopeTypeField(str, Enum): + TRIGGER_SCOPE = 'trigger_scope' + + +class WorkflowFlowsTriggerScopeObjectTypeField(str, Enum): + FOLDER = 'folder' + + +class WorkflowFlowsTriggerScopeObjectField(BaseObject): + _discriminator = 'type', {'folder'} + + def __init__( + self, + *, + type: Optional[WorkflowFlowsTriggerScopeObjectTypeField] = None, + id: Optional[str] = None, + **kwargs + ): + """ + :param type: The type of the object., defaults to None + :type type: Optional[WorkflowFlowsTriggerScopeObjectTypeField], optional + :param id: The id of the object., defaults to None + :type id: Optional[str], optional + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class WorkflowFlowsTriggerScopeField(BaseObject): + _discriminator = 'type', {'trigger_scope'} + + def __init__( + self, + *, + type: Optional[WorkflowFlowsTriggerScopeTypeField] = None, + ref: Optional[str] = None, + object: Optional[WorkflowFlowsTriggerScopeObjectField] = None, + **kwargs + ): + """ + :param type: The trigger scope's resource type., defaults to None + :type type: Optional[WorkflowFlowsTriggerScopeTypeField], optional + :param ref: Indicates the path of the condition value to check., defaults to None + :type ref: Optional[str], optional + :param object: The object the `ref` points to., defaults to None + :type object: Optional[WorkflowFlowsTriggerScopeObjectField], optional + """ + super().__init__(**kwargs) + self.type = type + self.ref = ref + self.object = object + + +class WorkflowFlowsTriggerField(BaseObject): + _discriminator = 'type', {'trigger'} + + def __init__( + self, + *, + type: Optional[WorkflowFlowsTriggerTypeField] = None, + trigger_type: Optional[WorkflowFlowsTriggerTriggerTypeField] = None, + scope: Optional[List[WorkflowFlowsTriggerScopeField]] = None, + **kwargs + ): + """ + :param type: The trigger's resource type., defaults to None + :type type: Optional[WorkflowFlowsTriggerTypeField], optional + :param trigger_type: The type of trigger selected for this flow., defaults to None + :type trigger_type: Optional[WorkflowFlowsTriggerTriggerTypeField], optional + :param scope: List of trigger scopes., defaults to None + :type scope: Optional[List[WorkflowFlowsTriggerScopeField]], optional + """ + super().__init__(**kwargs) + self.type = type + self.trigger_type = trigger_type + self.scope = scope + + +class WorkflowFlowsOutcomesTypeField(str, Enum): + OUTCOME = 'outcome' + + +class WorkflowFlowsOutcomesActionTypeField(str, Enum): + ADD_METADATA = 'add_metadata' + ASSIGN_TASK = 'assign_task' + COPY_FILE = 'copy_file' + COPY_FOLDER = 'copy_folder' + CREATE_FOLDER = 'create_folder' + DELETE_FILE = 'delete_file' + DELETE_FOLDER = 'delete_folder' + LOCK_FILE = 'lock_file' + MOVE_FILE = 'move_file' + MOVE_FOLDER = 'move_folder' + REMOVE_WATERMARK_FILE = 'remove_watermark_file' + RENAME_FOLDER = 'rename_folder' + RESTORE_FOLDER = 'restore_folder' + SHARE_FILE = 'share_file' + SHARE_FOLDER = 'share_folder' + UNLOCK_FILE = 'unlock_file' + UPLOAD_FILE = 'upload_file' + WAIT_FOR_TASK = 'wait_for_task' + WATERMARK_FILE = 'watermark_file' + GO_BACK_TO_STEP = 'go_back_to_step' + APPLY_FILE_CLASSIFICATION = 'apply_file_classification' + APPLY_FOLDER_CLASSIFICATION = 'apply_folder_classification' + SEND_NOTIFICATION = 'send_notification' + + +class WorkflowFlowsOutcomesIfRejectedTypeField(str, Enum): + OUTCOME = 'outcome' + + +class WorkflowFlowsOutcomesIfRejectedActionTypeField(str, Enum): + ADD_METADATA = 'add_metadata' + ASSIGN_TASK = 'assign_task' + COPY_FILE = 'copy_file' + COPY_FOLDER = 'copy_folder' + CREATE_FOLDER = 'create_folder' + DELETE_FILE = 'delete_file' + DELETE_FOLDER = 'delete_folder' + LOCK_FILE = 'lock_file' + MOVE_FILE = 'move_file' + MOVE_FOLDER = 'move_folder' + REMOVE_WATERMARK_FILE = 'remove_watermark_file' + RENAME_FOLDER = 'rename_folder' + RESTORE_FOLDER = 'restore_folder' + SHARE_FILE = 'share_file' + SHARE_FOLDER = 'share_folder' + UNLOCK_FILE = 'unlock_file' + UPLOAD_FILE = 'upload_file' + WAIT_FOR_TASK = 'wait_for_task' + WATERMARK_FILE = 'watermark_file' + GO_BACK_TO_STEP = 'go_back_to_step' + APPLY_FILE_CLASSIFICATION = 'apply_file_classification' + APPLY_FOLDER_CLASSIFICATION = 'apply_folder_classification' + SEND_NOTIFICATION = 'send_notification' + + +class WorkflowFlowsOutcomesIfRejectedField(BaseObject): + _discriminator = 'type', {'outcome'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[WorkflowFlowsOutcomesIfRejectedTypeField] = None, + name: Optional[str] = None, + action_type: Optional[WorkflowFlowsOutcomesIfRejectedActionTypeField] = None, + **kwargs + ): + """ + :param id: The identifier of the outcome., defaults to None + :type id: Optional[str], optional + :param type: The outcomes resource type., defaults to None + :type type: Optional[WorkflowFlowsOutcomesIfRejectedTypeField], optional + :param name: The name of the outcome., defaults to None + :type name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + self.action_type = action_type + + +class WorkflowFlowsOutcomesField(BaseObject): + _discriminator = 'type', {'outcome'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[WorkflowFlowsOutcomesTypeField] = None, + name: Optional[str] = None, + action_type: Optional[WorkflowFlowsOutcomesActionTypeField] = None, + if_rejected: Optional[List[WorkflowFlowsOutcomesIfRejectedField]] = None, + **kwargs + ): + """ + :param id: The identifier of the outcome., defaults to None + :type id: Optional[str], optional + :param type: The outcomes resource type., defaults to None + :type type: Optional[WorkflowFlowsOutcomesTypeField], optional + :param name: The name of the outcome., defaults to None + :type name: Optional[str], optional + :param if_rejected: If `action_type` is `assign_task` and the task is rejected, returns a + list of outcomes to complete., defaults to None + :type if_rejected: Optional[List[WorkflowFlowsOutcomesIfRejectedField]], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + self.action_type = action_type + self.if_rejected = if_rejected + + +class WorkflowFlowsField(BaseObject): + _discriminator = 'type', {'flow'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[WorkflowFlowsTypeField] = None, + trigger: Optional[WorkflowFlowsTriggerField] = None, + outcomes: Optional[List[WorkflowFlowsOutcomesField]] = None, + created_at: Optional[DateTime] = None, + created_by: Optional[UserBase] = None, + **kwargs + ): + """ + :param id: The identifier of the flow., defaults to None + :type id: Optional[str], optional + :param type: The flow's resource type., defaults to None + :type type: Optional[WorkflowFlowsTypeField], optional + :param created_at: When this flow was created., defaults to None + :type created_at: Optional[DateTime], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.trigger = trigger + self.outcomes = outcomes + self.created_at = created_at + self.created_by = created_by + + +class Workflow(WorkflowMini): + def __init__( + self, + *, + flows: Optional[List[WorkflowFlowsField]] = None, + id: Optional[str] = None, + type: Optional[WorkflowMiniTypeField] = None, + name: Optional[str] = None, + description: Optional[str] = None, + is_enabled: Optional[bool] = None, + **kwargs + ): + """ + :param flows: A list of flows assigned to a workflow., defaults to None + :type flows: Optional[List[WorkflowFlowsField]], optional + :param id: The unique identifier for the workflow., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `workflow`., defaults to None + :type type: Optional[WorkflowMiniTypeField], optional + :param name: The name of the workflow., defaults to None + :type name: Optional[str], optional + :param description: The description for a workflow., defaults to None + :type description: Optional[str], optional + :param is_enabled: Specifies if this workflow is enabled., defaults to None + :type is_enabled: Optional[bool], optional + """ + super().__init__( + id=id, + type=type, + name=name, + description=description, + is_enabled=is_enabled, + **kwargs + ) + self.flows = flows diff --git a/box_sdk_gen/schemas/workflow_mini.py b/box_sdk_gen/schemas/workflow_mini.py new file mode 100644 index 000000000..7b5f9e386 --- /dev/null +++ b/box_sdk_gen/schemas/workflow_mini.py @@ -0,0 +1,44 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class WorkflowMiniTypeField(str, Enum): + WORKFLOW = 'workflow' + + +class WorkflowMini(BaseObject): + _discriminator = 'type', {'workflow'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[WorkflowMiniTypeField] = None, + name: Optional[str] = None, + description: Optional[str] = None, + is_enabled: Optional[bool] = None, + **kwargs + ): + """ + :param id: The unique identifier for the workflow., defaults to None + :type id: Optional[str], optional + :param type: The value will always be `workflow`., defaults to None + :type type: Optional[WorkflowMiniTypeField], optional + :param name: The name of the workflow., defaults to None + :type name: Optional[str], optional + :param description: The description for a workflow., defaults to None + :type description: Optional[str], optional + :param is_enabled: Specifies if this workflow is enabled., defaults to None + :type is_enabled: Optional[bool], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.name = name + self.description = description + self.is_enabled = is_enabled diff --git a/box_sdk_gen/schemas/workflows.py b/box_sdk_gen/schemas/workflows.py new file mode 100644 index 000000000..0fdb1abad --- /dev/null +++ b/box_sdk_gen/schemas/workflows.py @@ -0,0 +1,38 @@ +from typing import Optional + +from typing import List + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.schemas.workflow import Workflow + +from box_sdk_gen.box.errors import BoxSDKError + + +class Workflows(BaseObject): + def __init__( + self, + *, + limit: Optional[int] = None, + next_marker: Optional[str] = None, + prev_marker: Optional[str] = None, + entries: Optional[List[Workflow]] = None, + **kwargs + ): + """ + :param limit: The limit that was used for these entries. This will be the same as the + `limit` query parameter unless that value exceeded the maximum value + allowed. The maximum value varies by API., defaults to None + :type limit: Optional[int], optional + :param next_marker: The marker for the start of the next page of results., defaults to None + :type next_marker: Optional[str], optional + :param prev_marker: The marker for the start of the previous page of results., defaults to None + :type prev_marker: Optional[str], optional + :param entries: A list of workflows., defaults to None + :type entries: Optional[List[Workflow]], optional + """ + super().__init__(**kwargs) + self.limit = limit + self.next_marker = next_marker + self.prev_marker = prev_marker + self.entries = entries diff --git a/box_sdk_gen/schemas/zip_download.py b/box_sdk_gen/schemas/zip_download.py new file mode 100644 index 000000000..37d5c22bd --- /dev/null +++ b/box_sdk_gen/schemas/zip_download.py @@ -0,0 +1,103 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from box_sdk_gen.box.errors import BoxSDKError + +from box_sdk_gen.internal.utils import DateTime + + +class ZipDownloadNameConflictsTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class ZipDownloadNameConflictsField(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__( + self, + *, + id: Optional[str] = None, + type: Optional[ZipDownloadNameConflictsTypeField] = None, + original_name: Optional[str] = None, + download_name: Optional[str] = None, + **kwargs + ): + """ + :param id: The identifier of the item., defaults to None + :type id: Optional[str], optional + :param type: The type of this item., defaults to None + :type type: Optional[ZipDownloadNameConflictsTypeField], optional + :param original_name: Box Developer Documentation., defaults to None + :type original_name: Optional[str], optional + :param download_name: The new name of this item as it will appear in the + downloaded `zip` archive., defaults to None + :type download_name: Optional[str], optional + """ + super().__init__(**kwargs) + self.id = id + self.type = type + self.original_name = original_name + self.download_name = download_name + + +class ZipDownload(BaseObject): + def __init__( + self, + *, + download_url: Optional[str] = None, + status_url: Optional[str] = None, + expires_at: Optional[DateTime] = None, + name_conflicts: Optional[List[List[ZipDownloadNameConflictsField]]] = None, + **kwargs + ): + """ + :param download_url: The URL that can be used to download the `zip` archive. A `Get` request to + this URL will start streaming the items requested. By default, this URL + is only valid for a few seconds, until the `expires_at` time, unless a + download is started after which it is valid for the duration of the + download. + + It is important to note that the domain and path of this URL might change + between API calls, and therefore it's important to use this URL as-is., defaults to None + :type download_url: Optional[str], optional + :param status_url: The URL that can be used to get the status of the `zip` archive being + downloaded. A `Get` request to this URL will return the number of files + in the archive as well as the number of items already downloaded or + skipped. By default, this URL is only valid for a few seconds, until the + `expires_at` time, unless a download is started after which the URL is + valid for 12 hours from the start of the download. + + It is important to note that the domain and path of this URL might change + between API calls, and therefore it's important to use this URL as-is., defaults to None + :type status_url: Optional[str], optional + :param expires_at: The time and date when this archive will expire. After this time the + `status_url` and `download_url` will return an error. + + By default, these URLs are only valid for a few seconds, unless a download + is started after which the `download_url` is valid for the duration of the + download, and the `status_url` is valid for 12 hours from the start of the + download., defaults to None + :type expires_at: Optional[DateTime], optional + :param name_conflicts: A list of conflicts that occurred when trying to create the archive. This + would occur when multiple items have been requested with the + same name. + + To solve these conflicts, the API will automatically rename an item + and return a mapping between the original item's name and its new + name. + + For every conflict, both files will be renamed and therefore this list + will always be a multiple of 2., defaults to None + :type name_conflicts: Optional[List[List[ZipDownloadNameConflictsField]]], optional + """ + super().__init__(**kwargs) + self.download_url = download_url + self.status_url = status_url + self.expires_at = expires_at + self.name_conflicts = name_conflicts diff --git a/box_sdk_gen/schemas/zip_download_request.py b/box_sdk_gen/schemas/zip_download_request.py new file mode 100644 index 000000000..45185b6f5 --- /dev/null +++ b/box_sdk_gen/schemas/zip_download_request.py @@ -0,0 +1,51 @@ +from enum import Enum + +from box_sdk_gen.internal.base_object import BaseObject + +from typing import List + +from typing import Optional + +from box_sdk_gen.box.errors import BoxSDKError + + +class ZipDownloadRequestItemsTypeField(str, Enum): + FILE = 'file' + FOLDER = 'folder' + + +class ZipDownloadRequestItemsField(BaseObject): + _discriminator = 'type', {'file', 'folder'} + + def __init__(self, type: ZipDownloadRequestItemsTypeField, id: str, **kwargs): + """ + :param type: The type of the item to add to the archive. + :type type: ZipDownloadRequestItemsTypeField + :param id: The identifier of the item to add to the archive. When this item is + a folder then this can not be the root folder with ID `0`. + :type id: str + """ + super().__init__(**kwargs) + self.type = type + self.id = id + + +class ZipDownloadRequest(BaseObject): + def __init__( + self, + items: List[ZipDownloadRequestItemsField], + *, + download_file_name: Optional[str] = None, + **kwargs + ): + """ + :param items: A list of items to add to the `zip` archive. These can + be folders or files. + :type items: List[ZipDownloadRequestItemsField] + :param download_file_name: The optional name of the `zip` archive. This name will be appended by the + `.zip` file extension, for example `January Financials.zip`., defaults to None + :type download_file_name: Optional[str], optional + """ + super().__init__(**kwargs) + self.items = items + self.download_file_name = download_file_name diff --git a/box_sdk_gen/schemas/zip_download_status.py b/box_sdk_gen/schemas/zip_download_status.py new file mode 100644 index 000000000..2923e3a0a --- /dev/null +++ b/box_sdk_gen/schemas/zip_download_status.py @@ -0,0 +1,50 @@ +from enum import Enum + +from typing import Optional + +from box_sdk_gen.internal.base_object import BaseObject + +from box_sdk_gen.box.errors import BoxSDKError + + +class ZipDownloadStatusStateField(str, Enum): + IN_PROGRESS = 'in_progress' + FAILED = 'failed' + SUCCEEDED = 'succeeded' + + +class ZipDownloadStatus(BaseObject): + def __init__( + self, + *, + total_file_count: Optional[int] = None, + downloaded_file_count: Optional[int] = None, + skipped_file_count: Optional[int] = None, + skipped_folder_count: Optional[int] = None, + state: Optional[ZipDownloadStatusStateField] = None, + **kwargs + ): + """ + :param total_file_count: The total number of files in the archive., defaults to None + :type total_file_count: Optional[int], optional + :param downloaded_file_count: The number of files that have already been downloaded., defaults to None + :type downloaded_file_count: Optional[int], optional + :param skipped_file_count: The number of files that have been skipped as they could not be + downloaded. In many cases this is due to permission issues that have + surfaced between the creation of the request for the archive and the + archive being downloaded., defaults to None + :type skipped_file_count: Optional[int], optional + :param skipped_folder_count: The number of folders that have been skipped as they could not be + downloaded. In many cases this is due to permission issues that have + surfaced between the creation of the request for the archive and the + archive being downloaded., defaults to None + :type skipped_folder_count: Optional[int], optional + :param state: The state of the archive being downloaded., defaults to None + :type state: Optional[ZipDownloadStatusStateField], optional + """ + super().__init__(**kwargs) + self.total_file_count = total_file_count + self.downloaded_file_count = downloaded_file_count + self.skipped_file_count = skipped_file_count + self.skipped_folder_count = skipped_folder_count + self.state = state diff --git a/box_sdk_gen/serialization/__init__.py b/box_sdk_gen/serialization/__init__.py new file mode 100644 index 000000000..c4b7d4cac --- /dev/null +++ b/box_sdk_gen/serialization/__init__.py @@ -0,0 +1 @@ +from box_sdk_gen.serialization.json import * diff --git a/box_sdk_gen/serialization/json.py b/box_sdk_gen/serialization/json.py new file mode 100644 index 000000000..7d7b580c0 --- /dev/null +++ b/box_sdk_gen/serialization/json.py @@ -0,0 +1,66 @@ +import json +from typing import Dict, get_origin, Union, Type +from urllib.parse import urlencode + +from ..internal.base_object import BaseObject + +SerializedData = Dict + + +def json_to_serialized_data(data: str) -> SerializedData: + return json.loads(data) + + +def sd_to_json(data: SerializedData) -> str: + return json.dumps(data) + + +def sd_to_url_params(data: SerializedData) -> str: + return urlencode(data) + + +def get_sd_value_by_key(data: SerializedData, key: str): + return data.get(key) + + +def serialize(obj: Union[BaseObject, dict, list]) -> SerializedData: + if isinstance(obj, dict): + obj = BaseObject(**obj).to_dict() + if isinstance(obj, BaseObject): + obj = obj.to_dict() + if isinstance(obj, list): + obj = [ + element.to_dict() if isinstance(element, BaseObject) else element + for element in obj + ] + return obj + + +def deserialize(value: SerializedData, type: Type[BaseObject]): + if get_origin(type) == Union: + type = BaseObject._deserialize_union('', value, type) + obj = type.from_dict(value) + obj._raw_data = value + return obj + + +def sanitized_value() -> str: + return '---[redacted]---' + + +def sanitize_serialized_data( + sd: SerializedData, keys_to_sanitize: Dict[str, str] +) -> SerializedData: + if not isinstance(sd, Dict): + return sd + sanitized_dictionary = {} + for key, value in sd.items(): + if key.lower() in keys_to_sanitize and isinstance(value, str): + sanitized_dictionary[key] = sanitized_value() + elif isinstance(value, Dict): + sanitized_dictionary[key] = sanitize_serialized_data( + value, keys_to_sanitize + ) + else: + sanitized_dictionary[key] = value + return sanitized_dictionary diff --git a/boxsdk/auth/__init__.py b/boxsdk/auth/__init__.py index 4ff79476d..694ececf0 100644 --- a/boxsdk/auth/__init__.py +++ b/boxsdk/auth/__init__.py @@ -1,11 +1,13 @@ from .cooperatively_managed_oauth2 import CooperativelyManagedOAuth2 from .developer_token_auth import DeveloperTokenAuth + try: from .jwt_auth import JWTAuth except ImportError: JWTAuth = None # If extras[jwt] are not installed, JWTAuth won't be available. from .ccg_auth import CCGAuth from .oauth2 import OAuth2 + try: from .redis_managed_oauth2 import RedisManagedOAuth2 except ImportError: diff --git a/boxsdk/auth/ccg_auth.py b/boxsdk/auth/ccg_auth.py index fb7155057..e49e6e692 100644 --- a/boxsdk/auth/ccg_auth.py +++ b/boxsdk/auth/ccg_auth.py @@ -7,7 +7,9 @@ class CCGAuth(ServerAuth): _GRANT_TYPE = 'client_credentials' - def _fetch_access_token(self, subject_id: str, subject_type: str, now_time: Optional[datetime] = None) -> str: + def _fetch_access_token( + self, subject_id: str, subject_type: str, now_time: Optional[datetime] = None + ) -> str: data = { 'grant_type': self._GRANT_TYPE, 'client_id': self._client_id, @@ -16,4 +18,6 @@ def _fetch_access_token(self, subject_id: str, subject_type: str, now_time: Opti 'box_subject_type': subject_type, } - return self.send_token_request(data, access_token=None, expect_refresh_token=False)[0] + return self.send_token_request( + data, access_token=None, expect_refresh_token=False + )[0] diff --git a/boxsdk/auth/cooperatively_managed_oauth2.py b/boxsdk/auth/cooperatively_managed_oauth2.py index f324a84e7..c054a60e1 100644 --- a/boxsdk/auth/cooperatively_managed_oauth2.py +++ b/boxsdk/auth/cooperatively_managed_oauth2.py @@ -8,7 +8,13 @@ class CooperativelyManagedOAuth2Mixin(OAuth2): Box SDK OAuth2 mixin. Allows for sharing auth tokens between multiple clients. """ - def __init__(self, retrieve_tokens: Callable[[], Tuple[str, str]] = None, *args: Any, **kwargs: Any): + + def __init__( + self, + retrieve_tokens: Callable[[], Tuple[str, str]] = None, + *args: Any, + **kwargs: Any + ): """ :param retrieve_tokens: Callback to get the current access/refresh token pair. diff --git a/boxsdk/auth/jwt_auth.py b/boxsdk/auth/jwt_auth.py index faf245828..320b73074 100644 --- a/boxsdk/auth/jwt_auth.py +++ b/boxsdk/auth/jwt_auth.py @@ -20,25 +20,26 @@ class JWTAuth(ServerAuth): """ Responsible for handling JWT Auth for Box Developer Edition. Can authenticate enterprise instances or app users. """ + _GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer' def __init__( - self, - client_id: str, - client_secret: str, - enterprise_id: Optional[str], - jwt_key_id: str, - rsa_private_key_file_sys_path: Optional[str] = None, - rsa_private_key_passphrase: Optional[Union[str, bytes]] = None, - user: Optional[Union[str, 'User']] = None, - store_tokens: Optional[Callable[[str, str], None]] = None, - box_device_id: str = '0', - box_device_name: str = '', - access_token: str = None, - session: Optional['Network'] = None, - jwt_algorithm: str = 'RS256', - rsa_private_key_data: Union[bytes, IOBase, RSAPrivateKey] = None, - **kwargs + self, + client_id: str, + client_secret: str, + enterprise_id: Optional[str], + jwt_key_id: str, + rsa_private_key_file_sys_path: Optional[str] = None, + rsa_private_key_passphrase: Optional[Union[str, bytes]] = None, + user: Optional[Union[str, 'User']] = None, + store_tokens: Optional[Callable[[str, str], None]] = None, + box_device_id: str = '0', + box_device_name: str = '', + access_token: str = None, + session: Optional['Network'] = None, + jwt_algorithm: str = 'RS256', + rsa_private_key_data: Union[bytes, IOBase, RSAPrivateKey] = None, + **kwargs, ): """Extends baseclass method. @@ -123,13 +124,15 @@ def __init__( access_token=access_token, refresh_token=None, session=session, - **kwargs + **kwargs, ) self._rsa_private_key = rsa_private_key self._jwt_algorithm = jwt_algorithm self._jwt_key_id = jwt_key_id - def _fetch_access_token(self, subject_id: str, subject_type: str, now_time: Optional[datetime] = None) -> str: + def _fetch_access_token( + self, subject_id: str, subject_type: str, now_time: Optional[datetime] = None + ) -> str: """ Construct the claims used for JWT auth and send a request to get a JWT. Pass an enterprise ID to get an enterprise token (which can be used to provision/deprovision users), @@ -149,7 +152,10 @@ def _fetch_access_token(self, subject_id: str, subject_type: str, now_time: Opti jti_length = system_random.randint(16, 128) ascii_alphabet = string.ascii_letters + string.digits ascii_len = len(ascii_alphabet) - jti = ''.join(ascii_alphabet[int(system_random.random() * ascii_len)] for _ in range(jti_length)) + jti = ''.join( + ascii_alphabet[int(system_random.random() * ascii_len)] + for _ in range(jti_length) + ) if now_time is None: now_time = datetime.utcnow() now_plus_30 = now_time + timedelta(seconds=30) @@ -178,17 +184,21 @@ def _fetch_access_token(self, subject_id: str, subject_type: str, now_time: Opti data['box_device_id'] = self._box_device_id if self._box_device_name: data['box_device_name'] = self._box_device_name - return self.send_token_request(data, access_token=None, expect_refresh_token=False)[0] + return self.send_token_request( + data, access_token=None, expect_refresh_token=False + )[0] @classmethod def _normalize_rsa_private_key( - cls, - file_sys_path: str, - data: Union[bytes, IOBase, RSAPrivateKey], - passphrase: Optional[Union[str, bytes]] = None + cls, + file_sys_path: str, + data: Union[bytes, IOBase, RSAPrivateKey], + passphrase: Optional[Union[str, bytes]] = None, ) -> Any: if len(list(filter(None, [file_sys_path, data]))) != 1: - raise TypeError("must pass exactly one of either rsa_private_key_file_sys_path or rsa_private_key_data") + raise TypeError( + "must pass exactly one of either rsa_private_key_file_sys_path or rsa_private_key_data" + ) if file_sys_path: with open(file_sys_path, 'rb') as key_file: data = key_file.read() @@ -236,7 +246,9 @@ def _normalize_rsa_private_key_passphrase(passphrase: Any): return passphrase @classmethod - def from_settings_dictionary(cls, settings_dictionary: dict, **kwargs: Any) -> 'JWTAuth': + def from_settings_dictionary( + cls, settings_dictionary: dict, **kwargs: Any + ) -> 'JWTAuth': """ Create an auth instance as defined by the given settings dictionary. @@ -251,14 +263,22 @@ def from_settings_dictionary(cls, settings_dictionary: dict, **kwargs: Any) -> ' client_id=settings_dictionary['boxAppSettings']['clientID'], client_secret=settings_dictionary['boxAppSettings']['clientSecret'], enterprise_id=settings_dictionary.get('enterpriseID', None), - jwt_key_id=settings_dictionary['boxAppSettings']['appAuth'].get('publicKeyID', None), - rsa_private_key_data=settings_dictionary['boxAppSettings']['appAuth'].get('privateKey', None), - rsa_private_key_passphrase=settings_dictionary['boxAppSettings']['appAuth'].get('passphrase', None), - **kwargs + jwt_key_id=settings_dictionary['boxAppSettings']['appAuth'].get( + 'publicKeyID', None + ), + rsa_private_key_data=settings_dictionary['boxAppSettings']['appAuth'].get( + 'privateKey', None + ), + rsa_private_key_passphrase=settings_dictionary['boxAppSettings'][ + 'appAuth' + ].get('passphrase', None), + **kwargs, ) @classmethod - def from_settings_file(cls, settings_file_sys_path: str, **kwargs: Any) -> 'JWTAuth': + def from_settings_file( + cls, settings_file_sys_path: str, **kwargs: Any + ) -> 'JWTAuth': """ Create an auth instance as defined by a JSON file downloaded from the Box Developer Console. See https://developer.box.com/en/guides/authentication/jwt/ for more information. diff --git a/boxsdk/auth/oauth2.py b/boxsdk/auth/oauth2.py index adbba8384..8a72c2b85 100644 --- a/boxsdk/auth/oauth2.py +++ b/boxsdk/auth/oauth2.py @@ -19,10 +19,11 @@ class TokenScope(TextEnum): - """ Scopes used for a downscope token request. + """Scopes used for a downscope token request. See https://developer.box.com/en/guides/authentication/access-tokens/downscope/. """ + ITEM_READ = 'item_read' ITEM_READWRITE = 'item_readwrite' ITEM_PREVIEW = 'item_preview' @@ -33,7 +34,7 @@ class TokenScope(TextEnum): class TokenResponse(BaseAPIJSONObject): - """ Represents the response for a token request. """ + """Represents the response for a token request.""" class OAuth2: @@ -47,16 +48,16 @@ class OAuth2: """ def __init__( - self, - client_id: Optional[str], - client_secret: Optional[str], - store_tokens: Optional[Callable[[str, str], None]] = None, - box_device_id: str = '0', - box_device_name: str = '', - access_token: Optional[str] = None, - refresh_token: Optional[str] = None, - session: Optional[Session] = None, - refresh_lock: Optional[ContextManager] = None, + self, + client_id: Optional[str], + client_secret: Optional[str], + store_tokens: Optional[Callable[[str, str], None]] = None, + box_device_id: str = '0', + box_device_name: str = '', + access_token: Optional[str] = None, + refresh_token: Optional[str] = None, + session: Optional[Session] = None, + refresh_lock: Optional[ContextManager] = None, ): """ :param client_id: @@ -138,9 +139,16 @@ def get_authorization_url(self, redirect_url: Optional[str]) -> Tuple[str, str]: params.append(('redirect_uri', redirect_url)) # `urlencode()` doesn't work with non-ASCII unicode characters, so # encode the parameters as ASCII bytes. - params = [(key.encode('utf-8'), value.encode('utf-8')) for (key, value) in params] + params = [ + (key.encode('utf-8'), value.encode('utf-8')) for (key, value) in params + ] query_string = urlencode(params) - return urlunsplit(('', '', self._api_config.OAUTH2_AUTHORIZE_URL, query_string, '')), csrf_token + return ( + urlunsplit( + ('', '', self._api_config.OAUTH2_AUTHORIZE_URL, query_string, '') + ), + csrf_token, + ) def authenticate(self, auth_code: Optional[str]) -> Tuple[str, str]: """ @@ -194,7 +202,9 @@ def _get_tokens(self) -> Tuple[Optional[str], Optional[str]]: """ return self._access_token, self._refresh_token - def refresh(self, access_token_to_refresh: Optional[str]) -> Tuple[str, Optional[str]]: + def refresh( + self, access_token_to_refresh: Optional[str] + ) -> Tuple[str, Optional[str]]: """ Refresh the access token and the refresh token and return the access_token, refresh_token tuple. The access token and refresh token will be stored by calling the `store_tokens` callback if provided in __init__. @@ -225,7 +235,7 @@ def refresh(self, access_token_to_refresh: Optional[str]) -> Tuple[str, Optional @staticmethod def _get_state_csrf_token() -> str: - """ Generate a random state CSRF token to be used in the authorization url. + """Generate a random state CSRF token to be used in the authorization url. Example: box_csrf_token_Iijw9aU31sNdgiQu :return: @@ -234,9 +244,13 @@ def _get_state_csrf_token() -> str: system_random = random.SystemRandom() ascii_alphabet = string.ascii_letters + string.digits ascii_len = len(ascii_alphabet) - return 'box_csrf_token_' + ''.join(ascii_alphabet[int(system_random.random() * ascii_len)] for _ in range(16)) + return 'box_csrf_token_' + ''.join( + ascii_alphabet[int(system_random.random() * ascii_len)] for _ in range(16) + ) - def _store_tokens(self, access_token: Optional[str], refresh_token: Optional[str]) -> None: + def _store_tokens( + self, access_token: Optional[str], refresh_token: Optional[str] + ) -> None: self._update_current_tokens(access_token, refresh_token) if self._store_tokens_callback is not None: self._store_tokens_callback(access_token, refresh_token) @@ -251,7 +265,9 @@ def _get_and_update_current_tokens(self) -> Tuple[Optional[str], Optional[str]]: self._update_current_tokens(*tokens) return tokens - def _update_current_tokens(self, access_token: Optional[str], refresh_token: Optional[str]) -> None: + def _update_current_tokens( + self, access_token: Optional[str], refresh_token: Optional[str] + ) -> None: """Store the latest tokens in this object's private attributes. :param access_token: @@ -265,10 +281,7 @@ def _update_current_tokens(self, access_token: Optional[str], refresh_token: Opt self._access_token, self._refresh_token = access_token, refresh_token def _execute_token_request( - self, - data: dict, - access_token: Optional[str], - expect_refresh_token: bool = True + self, data: dict, access_token: Optional[str], expect_refresh_token: bool = True ) -> TokenResponse: """ Send the request to acquire or refresh an access token. @@ -292,7 +305,9 @@ def _execute_token_request( access_token=access_token, ) except BoxAPIException as box_api_exception: - raise self._oauth_exception(box_api_exception.network_response, url) from box_api_exception + raise self._oauth_exception( + box_api_exception.network_response, url + ) from box_api_exception if not network_response.ok: raise self._oauth_exception(network_response, url) @@ -301,13 +316,17 @@ def _execute_token_request( except ValueError as value_exception: raise self._oauth_exception(network_response, url) from value_exception - if ('access_token' not in token_response) or (expect_refresh_token and 'refresh_token' not in token_response): + if ('access_token' not in token_response) or ( + expect_refresh_token and 'refresh_token' not in token_response + ): raise self._oauth_exception(network_response, url) return token_response @staticmethod - def _oauth_exception(network_response: Union['NetworkResponse', 'BoxResponse'], url: str) -> BoxOAuthException: + def _oauth_exception( + network_response: Union['NetworkResponse', 'BoxResponse'], url: str + ) -> BoxOAuthException: """ Create a BoxOAuthException instance to raise. If the error response is JSON, parse it and include the code and message in the exception. @@ -320,19 +339,19 @@ def _oauth_exception(network_response: Union['NetworkResponse', 'BoxResponse'], ) if is_json_response(network_response): json_response = network_response.json() - exception_kwargs.update(dict( - code=json_response.get('code') or json_response.get('error'), - message=json_response.get('message') or json_response.get('error_description'), - )) + exception_kwargs.update( + dict( + code=json_response.get('code') or json_response.get('error'), + message=json_response.get('message') + or json_response.get('error_description'), + ) + ) else: exception_kwargs['message'] = network_response.content return BoxOAuthException(**exception_kwargs) def send_token_request( - self, - data: dict, - access_token: Optional[str], - expect_refresh_token: bool = True + self, data: dict, access_token: Optional[str], expect_refresh_token: bool = True ) -> Tuple[str, str]: """ Send the request to acquire or refresh an access token, and store the tokens. @@ -346,9 +365,13 @@ def send_token_request( :return: The access token and refresh token. """ - token_response = self._execute_token_request(data, access_token, expect_refresh_token) + token_response = self._execute_token_request( + data, access_token, expect_refresh_token + ) # pylint:disable=no-member - refresh_token = token_response.refresh_token if 'refresh_token' in token_response else None + refresh_token = ( + token_response.refresh_token if 'refresh_token' in token_response else None + ) self._store_tokens(token_response.access_token, refresh_token) return self._access_token, self._refresh_token @@ -374,7 +397,9 @@ def revoke(self) -> None: access_token=access_token, ) except BoxAPIException as box_api_exception: - raise self._oauth_exception(box_api_exception.network_response, url) from box_api_exception + raise self._oauth_exception( + box_api_exception.network_response, url + ) from box_api_exception if not network_response.ok: raise BoxOAuthException( diff --git a/boxsdk/auth/redis_managed_jwt_auth.py b/boxsdk/auth/redis_managed_jwt_auth.py index d93c400b1..3a0ffc7d5 100644 --- a/boxsdk/auth/redis_managed_jwt_auth.py +++ b/boxsdk/auth/redis_managed_jwt_auth.py @@ -8,6 +8,7 @@ class RedisManagedJWTAuth(RedisManagedOAuth2Mixin, JWTAuth): """ JWT Auth subclass which uses Redis to manage access tokens. """ + def _auth_with_jwt(self, sub: str, sub_type: str) -> Tuple[str, None]: """ Base class override. Returns the access token in a tuple to match the OAuth2 interface. diff --git a/boxsdk/auth/redis_managed_oauth2.py b/boxsdk/auth/redis_managed_oauth2.py index 553f04927..a396f83bd 100644 --- a/boxsdk/auth/redis_managed_oauth2.py +++ b/boxsdk/auth/redis_managed_oauth2.py @@ -12,7 +12,14 @@ class RedisManagedOAuth2Mixin(OAuth2): Box SDK OAuth2 subclass. Allows for storing auth tokens in redis. """ - def __init__(self, unique_id: str = uuid4(), redis_server: Redis = None, *args: Any, **kwargs: Any): + + def __init__( + self, + unique_id: str = uuid4(), + redis_server: Redis = None, + *args: Any, + **kwargs: Any, + ): """ :param unique_id: An identifier for this auth object. Auth instances which wish to share tokens must use the same ID. @@ -48,7 +55,9 @@ def _store_tokens(self, access_token: str, refresh_token: str) -> None: Saves the refreshed tokens in redis. """ super()._store_tokens(access_token, refresh_token) - self._redis_server.hmset(self._unique_id, {'access': access_token, 'refresh': refresh_token}) + self._redis_server.hmset( + self._unique_id, {'access': access_token, 'refresh': refresh_token} + ) class RedisManagedOAuth2(RedisManagedOAuth2Mixin): diff --git a/boxsdk/auth/remote_managed_oauth2.py b/boxsdk/auth/remote_managed_oauth2.py index b716fbef4..918a1bd87 100644 --- a/boxsdk/auth/remote_managed_oauth2.py +++ b/boxsdk/auth/remote_managed_oauth2.py @@ -9,7 +9,13 @@ class RemoteOAuth2Mixin(OAuth2): Allows for storing auth tokens remotely. """ - def __init__(self, retrieve_access_token: Callable[[str], str] = None, *args: Any, **kwargs: Any): + + def __init__( + self, + retrieve_access_token: Callable[[str], str] = None, + *args: Any, + **kwargs: Any + ): """ :param retrieve_access_token: Callback to exchange an existing access token for a new one. diff --git a/boxsdk/auth/server_auth.py b/boxsdk/auth/server_auth.py index 59e46e3a7..9ac7648bc 100644 --- a/boxsdk/auth/server_auth.py +++ b/boxsdk/auth/server_auth.py @@ -17,12 +17,12 @@ class ServerAuth(ABC, OAuth2): ENTERPRISE_SUBJECT_TYPE = 'enterprise' def __init__( - self, - client_id: str, - client_secret: str, - enterprise_id: Optional[str] = None, - user: Optional[Union[str, 'User']] = None, - **kwargs: Any + self, + client_id: str, + client_secret: str, + enterprise_id: Optional[str] = None, + user: Optional[Union[str, 'User']] = None, + **kwargs: Any, ): super().__init__(client_id=client_id, client_secret=client_secret, **kwargs) self._enterprise_id = enterprise_id @@ -66,7 +66,9 @@ def authenticate_user(self, user: Union[str, 'User'] = None) -> str: """ sub = self._normalize_user_id(user) or self._user_id if not sub: - raise ValueError("authenticate_user: Requires the user ID, but it was not provided.") + raise ValueError( + "authenticate_user: Requires the user ID, but it was not provided." + ) self._user_id = sub return self._authenticate(sub, self.USER_SUBJECT_TYPE) @@ -90,7 +92,9 @@ def authenticate_instance(self, enterprise: Optional[str] = None) -> str: """ enterprises = [enterprise, self._enterprise_id] if not any(enterprises): - raise ValueError("authenticate_instance: Requires the enterprise ID, but it was not provided.") + raise ValueError( + "authenticate_instance: Requires the enterprise ID, but it was not provided." + ) if all(enterprises) and (enterprise != self._enterprise_id): raise ValueError( f"authenticate_instance: Given enterprise ID {enterprise!r}, " @@ -131,21 +135,24 @@ def _authenticate(self, subject_id: str, subject_type: str) -> str: if code == 429 or code >= 500: date = None - elif box_datetime is not None and self._is_auth_error_retryable(network_response): + elif box_datetime is not None and self._is_auth_error_retryable( + network_response + ): date = box_datetime else: raise ex time_delay = self._session.get_retry_after_time( - attempt_number, - network_response.headers.get('Retry-After', None) + attempt_number, network_response.headers.get('Retry-After', None) ) time.sleep(time_delay) attempt_number += 1 self._logger.debug('Retrying authentication request') @abstractmethod - def _fetch_access_token(self, subject_id: str, subject_type: str, now_time: Optional[datetime] = None) -> str: + def _fetch_access_token( + self, subject_id: str, subject_type: str, now_time: Optional[datetime] = None + ) -> str: pass @staticmethod @@ -187,8 +194,11 @@ def _is_auth_error_retryable(network_response: 'NetworkResponse') -> bool: return False error_code = json_response.get('error', '') error_description = json_response.get('error_description', '') - return status_code == 400 and error_code == 'invalid_grant' \ + return ( + status_code == 400 + and error_code == 'invalid_grant' and ('exp' in error_description or 'jti' in error_description) + ) @classmethod def _normalize_user_id(cls, user: Any) -> Optional[str]: diff --git a/boxsdk/client/client.py b/boxsdk/client/client.py index e6b12049a..4f2c7b80c 100644 --- a/boxsdk/client/client.py +++ b/boxsdk/client/client.py @@ -12,7 +12,9 @@ from ..object.events import Events from ..object.collaboration_allowlist import CollaborationAllowlist from ..object.trash import Trash -from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from ..pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from ..pagination.marker_based_object_collection import MarkerBasedObjectCollection from ..util.datetime_formatter import normalize_date_to_rfc3339_format from ..util.shared_link import get_shared_link_header @@ -30,7 +32,9 @@ from boxsdk.object.legal_hold import LegalHold from boxsdk.object.legal_hold_policy_assignment import LegalHoldPolicyAssignment from boxsdk.object.legal_hold_policy import LegalHoldPolicy - from boxsdk.object.collaboration_allowlist_exempt_target import CollaborationAllowlistExemptTarget + from boxsdk.object.collaboration_allowlist_exempt_target import ( + CollaborationAllowlistExemptTarget, + ) from boxsdk.object.collaboration_allowlist_entry import CollaborationAllowlistEntry from boxsdk.object.collaboration import Collaboration from boxsdk.object.group import Group @@ -52,7 +56,11 @@ from boxsdk.object.task_assignment import TaskAssignment from boxsdk.object.task import Task from boxsdk.object.terms_of_service_user_status import TermsOfServiceUserStatus - from boxsdk.object.terms_of_service import TermsOfService, TermsOfServiceType, TermsOfServiceStatus + from boxsdk.object.terms_of_service import ( + TermsOfService, + TermsOfServiceType, + TermsOfServiceStatus, + ) from boxsdk.object.storage_policy_assignment import StoragePolicyAssignment from boxsdk.object.storage_policy import StoragePolicy from boxsdk.object.webhook import Webhook @@ -75,13 +83,18 @@ def __init__(self, oauth: 'OAuth2', session: Session = None): The session object to use. If None is provided then an instance of :class:`AuthorizedSession` will be used. """ super().__init__() - warn('Package \'boxsdk\' is going to be deprecated soon. Please use \'box-sdk-gen\' instead.', DeprecationWarning) + warn( + 'Package \'boxsdk\' is going to be deprecated soon. Please use \'box-sdk-gen\' instead.', + DeprecationWarning, + ) self._oauth = oauth if session is not None: self._session = session else: session = session or self.unauthorized_session_class() - self._session = self.authorized_session_class(self._oauth, **session.get_constructor_kwargs()) + self._session = self.authorized_session_class( + self._oauth, **session.get_constructor_kwargs() + ) @property def auth(self) -> 'OAuth2': @@ -99,8 +112,7 @@ def session(self) -> Session: @property def translator(self) -> 'Translator': - """The translator used for translating Box API JSON responses into `BaseAPIJSONObject` smart objects. - """ + """The translator used for translating Box API JSON responses into `BaseAPIJSONObject` smart objects.""" return self._session.translator def folder(self, folder_id: str) -> 'Folder': @@ -140,7 +152,9 @@ def file_request(self, request_id: str) -> 'FileRequest': :return: A :class:`FileRequest` object with the given file request id. """ - return self.translator.get('file_request')(session=self._session, object_id=request_id) + return self.translator.get('file_request')( + session=self._session, object_id=request_id + ) def file_version(self, version_id: str) -> 'FileVersion': """ @@ -151,7 +165,9 @@ def file_version(self, version_id: str) -> 'FileVersion': :return: A :class:`FileVersion` object with the given file version id. """ - return self.translator.get('file_version')(session=self._session, object_id=version_id) + return self.translator.get('file_version')( + session=self._session, object_id=version_id + ) def upload_session(self, session_id: str) -> 'UploadSession': """ @@ -162,7 +178,9 @@ def upload_session(self, session_id: str) -> 'UploadSession': :return: A :class:`UploadSession` object with the given session id. """ - return self.translator.get('upload_session')(session=self._session, object_id=session_id) + return self.translator.get('upload_session')( + session=self._session, object_id=session_id + ) def comment(self, comment_id: str) -> 'Comment': """ @@ -173,7 +191,9 @@ def comment(self, comment_id: str) -> 'Comment': :return: A :class:`Comment` object with the given comment ID. """ - return self.translator.get('comment')(session=self._session, object_id=comment_id) + return self.translator.get('comment')( + session=self._session, object_id=comment_id + ) def user(self, user_id: str = 'me') -> 'User': """ @@ -206,7 +226,9 @@ def email_alias(self, alias_id: str) -> 'EmailAlias': :return: A :class:`EmailAlias` object with the given entry ID. """ - return self.translator.get('email_alias')(session=self._session, object_id=alias_id) + return self.translator.get('email_alias')( + session=self._session, object_id=alias_id + ) def group(self, group_id: str) -> 'Group': """ @@ -228,7 +250,9 @@ def collaboration(self, collab_id: str) -> 'Collaboration': :return: A :class:`Collaboration` object with the given group id. """ - return self.translator.get('collaboration')(session=self._session, object_id=collab_id) + return self.translator.get('collaboration')( + session=self._session, object_id=collab_id + ) def collaboration_allowlist(self): """ @@ -239,7 +263,9 @@ def collaboration_allowlist(self): """ return CollaborationAllowlist(self._session) - def collaboration_allowlist_entry(self, entry_id: str) -> 'CollaborationAllowlistEntry': + def collaboration_allowlist_entry( + self, entry_id: str + ) -> 'CollaborationAllowlistEntry': """ Initialize a :class:`CollaborationAllowlistEntry` object, whose box id is entry_id. @@ -248,9 +274,13 @@ def collaboration_allowlist_entry(self, entry_id: str) -> 'CollaborationAllowlis :return: A :class:`CollaborationAllowlistEntry` object with the given entry id. """ - return self.translator.get('collaboration_whitelist_entry')(session=self._session, object_id=entry_id) + return self.translator.get('collaboration_whitelist_entry')( + session=self._session, object_id=entry_id + ) - def collaboration_allowlist_exempt_target(self, exemption_id: str) -> 'CollaborationAllowlistExemptTarget': + def collaboration_allowlist_exempt_target( + self, exemption_id: str + ) -> 'CollaborationAllowlistExemptTarget': """ Initialize a :class:`CollaborationAllowlistExemptTarget` object, whose box id is target_id. @@ -260,8 +290,7 @@ def collaboration_allowlist_exempt_target(self, exemption_id: str) -> 'Collabora A :class:`CollaborationAllowlistExemptTarget` object with the given target id. """ return self.translator.get('collaboration_whitelist_exempt_target')( - session=self._session, - object_id=exemption_id + session=self._session, object_id=exemption_id ) def trash(self) -> Trash: @@ -282,9 +311,13 @@ def legal_hold_policy(self, policy_id: str) -> 'LegalHoldPolicy': :return: A :class:`LegalHoldPolicy` object with the given entry ID. """ - return self.translator.get('legal_hold_policy')(session=self._session, object_id=policy_id) + return self.translator.get('legal_hold_policy')( + session=self._session, object_id=policy_id + ) - def legal_hold_policy_assignment(self, policy_assignment_id: str) -> 'LegalHoldPolicyAssignment': + def legal_hold_policy_assignment( + self, policy_assignment_id: str + ) -> 'LegalHoldPolicyAssignment': """ Initialize a :class:`LegalHoldPolicyAssignment` object, whose box id is policy_assignment_id. @@ -293,7 +326,9 @@ def legal_hold_policy_assignment(self, policy_assignment_id: str) -> 'LegalHoldP :return: A :class:`LegalHoldPolicyAssignment` object with the given entry ID. """ - return self.translator.get('legal_hold_policy_assignment')(session=self._session, object_id=policy_assignment_id) + return self.translator.get('legal_hold_policy_assignment')( + session=self._session, object_id=policy_assignment_id + ) def legal_hold(self, hold_id: str) -> 'LegalHold': """ @@ -304,16 +339,18 @@ def legal_hold(self, hold_id: str) -> 'LegalHold': :return: A :class:`LegalHold` object with the given entry ID. """ - return self.translator.get('legal_hold')(session=self._session, object_id=hold_id) + return self.translator.get('legal_hold')( + session=self._session, object_id=hold_id + ) @api_call def create_legal_hold_policy( - self, - policy_name: str, - description: Optional[str] = None, - filter_started_at: Union[datetime, str] = None, - filter_ended_at: Union[datetime, str] = None, - is_ongoing: Optional[bool] = None + self, + policy_name: str, + description: Optional[str] = None, + filter_started_at: Union[datetime, str] = None, + filter_ended_at: Union[datetime, str] = None, + is_ongoing: Optional[bool] = None, ) -> 'LegalHoldPolicy': """ Create a legal hold policy. @@ -339,9 +376,13 @@ def create_legal_hold_policy( if description is not None: policy_attributes['description'] = description if filter_started_at is not None: - policy_attributes['filter_started_at'] = normalize_date_to_rfc3339_format(filter_started_at) + policy_attributes['filter_started_at'] = normalize_date_to_rfc3339_format( + filter_started_at + ) if filter_ended_at is not None: - policy_attributes['filter_ended_at'] = normalize_date_to_rfc3339_format(filter_ended_at) + policy_attributes['filter_ended_at'] = normalize_date_to_rfc3339_format( + filter_ended_at + ) if is_ongoing is not None: policy_attributes['is_ongoing'] = is_ongoing box_response = self._session.post(url, data=json.dumps(policy_attributes)) @@ -353,11 +394,11 @@ def create_legal_hold_policy( @api_call def get_legal_hold_policies( - self, - policy_name: Optional[str] = None, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + policy_name: Optional[str] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the legal hold policy using limit-offset paging. @@ -395,14 +436,13 @@ def collection(self, collection_id: str) -> 'Collection': :return: A :class:`Collection` object with the given collection ID. """ - return self.translator.get('collection')(session=self._session, object_id=collection_id) + return self.translator.get('collection')( + session=self._session, object_id=collection_id + ) @api_call def collections( - self, - limit: Optional[int] = None, - offset: int = 0, - fields: Iterable[str] = None + self, limit: Optional[int] = None, offset: int = 0, fields: Iterable[str] = None ) -> 'BoxObjectCollection': """ Get a list of collections for the current user. @@ -432,7 +472,9 @@ def enterprise(self, enterprise_id: str) -> 'Enterprise': :return: A :class:`Enterprise` object with the given enterprise ID. """ - return self.translator.get('enterprise')(session=self._session, object_id=enterprise_id) + return self.translator.get('enterprise')( + session=self._session, object_id=enterprise_id + ) @api_call def get_current_enterprise(self) -> 'Enterprise': @@ -451,14 +493,14 @@ def get_current_enterprise(self) -> 'Enterprise': @api_call def users( - self, - limit: Optional[int] = None, - offset: int = 0, - filter_term: Optional[str] = None, - user_type: Optional[str] = None, - fields: Iterable[str] = None, - use_marker: bool = False, - marker: Optional[str] = None + self, + limit: Optional[int] = None, + offset: int = 0, + filter_term: Optional[str] = None, + user_type: Optional[str] = None, + fields: Iterable[str] = None, + use_marker: bool = False, + marker: Optional[str] = None, ) -> Iterable['User']: """ Get a list of all users for the Enterprise along with their user_id, public_name, and login. @@ -540,11 +582,11 @@ def group_membership(self, group_membership_id: str) -> 'GroupMembership': @api_call def get_groups( - self, - name: Optional[str] = None, - limit: Optional[int] = None, - offset: Optional[int] = None, - fields: Iterable[str] = None + self, + name: Optional[str] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + fields: Iterable[str] = None, ) -> Iterable['Group']: """ Get a list of all groups for the current user. @@ -583,10 +625,14 @@ def webhook(self, webhook_id: str) -> 'Webhook': :return: A :class:`Webhook` object with the given entry ID. """ - return self.translator.get('webhook')(session=self._session, object_id=webhook_id) + return self.translator.get('webhook')( + session=self._session, object_id=webhook_id + ) @api_call - def create_webhook(self, target: Union['File', 'Folder'], triggers: Union[list, str], address: str) -> 'Webhook': + def create_webhook( + self, target: Union['File', 'Folder'], triggers: Union[list, str], address: str + ) -> 'Webhook': """ Create a webhook on the given file. @@ -617,10 +663,10 @@ def create_webhook(self, target: Union['File', 'Folder'], triggers: Union[list, @api_call def get_webhooks( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get all webhooks in an enterprise. @@ -644,14 +690,14 @@ def get_webhooks( @api_call def create_group( - self, - name: str, - provenance: Optional[str] = None, - external_sync_identifier: Optional[str] = None, - description: Optional[str] = None, - invitability_level: str = None, - member_viewability_level: str = None, - fields: Iterable[str] = None, + self, + name: str, + provenance: Optional[str] = None, + external_sync_identifier: Optional[str] = None, + description: Optional[str] = None, + invitability_level: str = None, + member_viewability_level: str = None, + fields: Iterable[str] = None, ) -> 'Group': """ Create a group with the given name. @@ -692,7 +738,9 @@ def create_group( body_attributes['member_viewability_level'] = member_viewability_level if fields is not None: additional_params['fields'] = ','.join(fields) - box_response = self._session.post(url, data=json.dumps(body_attributes), params=additional_params) + box_response = self._session.post( + url, data=json.dumps(body_attributes), params=additional_params + ) response = box_response.json() return self.translator.translate( session=self._session, @@ -708,9 +756,13 @@ def storage_policy(self, policy_id: str) -> 'StoragePolicy': :return: A :class:`StoragePolicy` object with the given entry ID. """ - return self.translator.get('storage_policy')(session=self._session, object_id=policy_id) + return self.translator.get('storage_policy')( + session=self._session, object_id=policy_id + ) - def storage_policy_assignment(self, assignment_id: str) -> 'StoragePolicyAssignment': + def storage_policy_assignment( + self, assignment_id: str + ) -> 'StoragePolicyAssignment': """ Initialize a :class:`StoragePolicyAssignment` object, whose box id is assignment_id. @@ -719,13 +771,15 @@ def storage_policy_assignment(self, assignment_id: str) -> 'StoragePolicyAssignm :return: A :class:`StoragePolicyAssignment` object with the given entry ID. """ - return self.translator.get('storage_policy_assignment')(session=self._session, object_id=assignment_id) + return self.translator.get('storage_policy_assignment')( + session=self._session, object_id=assignment_id + ) def get_storage_policies( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the storage policy using marker-based paging. @@ -757,9 +811,13 @@ def terms_of_service(self, tos_id: str) -> 'TermsOfService': :return: A :class:`TermsOfService` object with the given terms of service id. """ - return self.translator.get('terms_of_service')(session=self._session, object_id=tos_id) + return self.translator.get('terms_of_service')( + session=self._session, object_id=tos_id + ) - def terms_of_service_user_status(self, tos_user_status_id: str) -> 'TermsOfServiceUserStatus': + def terms_of_service_user_status( + self, tos_user_status_id: str + ) -> 'TermsOfServiceUserStatus': """ Initialize a :class:`TermsOfServiceUserStatus` object, whose box id is tos_user_status_id. @@ -768,13 +826,15 @@ def terms_of_service_user_status(self, tos_user_status_id: str) -> 'TermsOfServi :return: A :class:`TermsOfServiceUserStatus` object with the given terms of service user status id. """ - return self.translator.get('terms_of_service_user_status')(session=self._session, object_id=tos_user_status_id) + return self.translator.get('terms_of_service_user_status')( + session=self._session, object_id=tos_user_status_id + ) def get_terms_of_services( - self, - tos_type: 'TermsOfServiceType' = None, - limit: Optional[int] = None, - fields: Iterable[str] = None + self, + tos_type: 'TermsOfServiceType' = None, + limit: Optional[int] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the terms of service using limit-offset paging. @@ -821,7 +881,9 @@ def task_assignment(self, assignment_id: str) -> 'TaskAssignment': :return: A :class:`TaskAssignment` object with the given entry ID. """ - return self.translator.get('task_assignment')(session=self._session, object_id=assignment_id) + return self.translator.get('task_assignment')( + session=self._session, object_id=assignment_id + ) def retention_policy(self, retention_id: str) -> 'RetentionPolicy': """ @@ -832,7 +894,9 @@ def retention_policy(self, retention_id: str) -> 'RetentionPolicy': :return: A :class:`RetentionPolicy` object with the given entry ID. """ - return self.translator.get('retention_policy')(session=self._session, object_id=retention_id) + return self.translator.get('retention_policy')( + session=self._session, object_id=retention_id + ) def file_version_retention(self, retention_id: str) -> 'FileVersionRetention': """ @@ -843,9 +907,13 @@ def file_version_retention(self, retention_id: str) -> 'FileVersionRetention': :return: A :class:`FileVersionRetention` object with the given retention ID. """ - return self.translator.get('file_version_retention')(session=self._session, object_id=retention_id) + return self.translator.get('file_version_retention')( + session=self._session, object_id=retention_id + ) - def retention_policy_assignment(self, assignment_id: str) -> 'RetentionPolicyAssignment': + def retention_policy_assignment( + self, assignment_id: str + ) -> 'RetentionPolicyAssignment': """ Initialize a :class:`RetentionPolicyAssignment` object, whose box id is assignment_id. @@ -854,19 +922,21 @@ def retention_policy_assignment(self, assignment_id: str) -> 'RetentionPolicyAss :return: A :class:`RetentionPolicyAssignment` object with the given assignment ID. """ - return self.translator.get('retention_policy_assignment')(session=self._session, object_id=assignment_id) + return self.translator.get('retention_policy_assignment')( + session=self._session, object_id=assignment_id + ) @api_call def create_retention_policy( - self, - policy_name: str, - disposition_action: str, - retention_length: Union[int, float], - can_owner_extend_retention: Optional[bool] = None, - are_owners_notified: Optional[bool] = None, - custom_notification_recipients: Iterable['User'] = None, - retention_type: Optional[str] = None, - description: Optional[str] = None, + self, + policy_name: str, + disposition_action: str, + retention_length: Union[int, float], + can_owner_extend_retention: Optional[bool] = None, + are_owners_notified: Optional[bool] = None, + custom_notification_recipients: Iterable['User'] = None, + retention_type: Optional[str] = None, + description: Optional[str] = None, ) -> 'RetentionPolicy': """ Create a retention policy for the given enterprise. @@ -908,11 +978,16 @@ def create_retention_policy( retention_attributes['policy_type'] = 'finite' retention_attributes['retention_length'] = retention_length if can_owner_extend_retention is not None: - retention_attributes['can_owner_extend_retention'] = can_owner_extend_retention + retention_attributes['can_owner_extend_retention'] = ( + can_owner_extend_retention + ) if are_owners_notified is not None: retention_attributes['are_owners_notified'] = are_owners_notified if custom_notification_recipients is not None: - user_list = [{'type': user.object_type, 'id': user.object_id} for user in custom_notification_recipients] + user_list = [ + {'type': user.object_type, 'id': user.object_id} + for user in custom_notification_recipients + ] retention_attributes['custom_notification_recipients'] = user_list if retention_type is not None: retention_attributes['retention_type'] = retention_type @@ -921,19 +996,18 @@ def create_retention_policy( box_response = self._session.post(url, data=json.dumps(retention_attributes)) response = box_response.json() return self.translator.translate( - session=self._session, - response_object=response + session=self._session, response_object=response ) @api_call def get_retention_policies( - self, - policy_name: Optional[str] = None, - policy_type: Optional[str] = None, - user: Optional['User'] = None, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None, + self, + policy_name: Optional[str] = None, + policy_type: Optional[str] = None, + user: Optional['User'] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the retention policy using marker-based paging. @@ -971,10 +1045,7 @@ def get_retention_policies( ) def create_terms_of_service( - self, - status: 'TermsOfServiceStatus', - tos_type: 'TermsOfServiceType', - text: str + self, status: 'TermsOfServiceStatus', tos_type: 'TermsOfServiceType', text: str ) -> 'TermsOfService': """ Create a terms of service. @@ -989,11 +1060,7 @@ def create_terms_of_service( A newly created :class:`TermsOfService` object """ url = self.get_url('terms_of_services') - body = { - 'status': status, - 'tos_type': tos_type, - 'text': text - } + body = {'status': status, 'tos_type': tos_type, 'text': text} box_response = self._session.post(url, data=json.dumps(body)) response = box_response.json() return self.translator.translate( @@ -1001,20 +1068,22 @@ def create_terms_of_service( response_object=response, ) - @deprecated("Use RetentionPolicyAssignment.get_files_under_retention " - "or RetentionPolicyAssignment.get_file_versions_under_retention instead") + @deprecated( + "Use RetentionPolicyAssignment.get_files_under_retention " + "or RetentionPolicyAssignment.get_file_versions_under_retention instead" + ) @api_call def get_file_version_retentions( - self, - target_file: Optional['File'] = None, - file_version: Optional['FileVersion'] = None, - policy: Optional['RetentionPolicy'] = None, - disposition_action: Optional[str] = None, - disposition_before: Optional[str] = None, - disposition_after: Optional[str] = None, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None, + self, + target_file: Optional['File'] = None, + file_version: Optional['FileVersion'] = None, + policy: Optional['RetentionPolicy'] = None, + disposition_action: Optional[str] = None, + disposition_before: Optional[str] = None, + disposition_after: Optional[str] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the file version retention. @@ -1071,15 +1140,17 @@ def web_link(self, web_link_id: str) -> 'WebLink': :return: A :class:`WebLink` object with the given entry ID. """ - return self.translator.get('web_link')(session=self._session, object_id=web_link_id) + return self.translator.get('web_link')( + session=self._session, object_id=web_link_id + ) @api_call def get_recent_items( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None, - **collection_kwargs: Any + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, + **collection_kwargs: Any, ) -> MarkerBasedObjectCollection: """ Get the user's recently accessed items. @@ -1103,7 +1174,7 @@ def get_recent_items( limit=limit, fields=fields, marker=marker, - **collection_kwargs + **collection_kwargs, ) @api_call @@ -1147,7 +1218,9 @@ def make_request(self, method: str, url: str, **kwargs: Any) -> 'BoxResponse': return self._session.request(method, url, **kwargs) @api_call - def create_user(self, name: str, login: Optional[str] = None, **user_attributes: Any) -> 'User': + def create_user( + self, name: str, login: Optional[str] = None, **user_attributes: Any + ) -> 'User': """ Create a new user. Can only be used if the current user is an enterprise admin, or the current authorization scope is a Box developer edition instance. @@ -1177,10 +1250,10 @@ def create_user(self, name: str, login: Optional[str] = None, **user_attributes: @api_call def get_pending_collaborations( - self, - limit: Optional[int] = None, - offset: Optional[int] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + offset: Optional[int] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the pending collaborations using limit-offset paging. @@ -1206,11 +1279,11 @@ def get_pending_collaborations( @api_call def downscope_token( - self, - scopes: Iterable['TokenScope'], - item: 'Item' = None, - additional_data: dict = None, - shared_link: str = None + self, + scopes: Iterable['TokenScope'], + item: 'Item' = None, + additional_data: dict = None, + shared_link: str = None, ) -> TokenResponse: """ Generate a downscoped token for the provided file or folder with the provided scopes. @@ -1275,15 +1348,17 @@ def device_pinner(self, device_pin_id: str) -> 'DevicePinner': :return: A :class:`DevicePinner` object with the given entry ID. """ - return self.translator.get('device_pinner')(session=self._session, object_id=device_pin_id) + return self.translator.get('device_pinner')( + session=self._session, object_id=device_pin_id + ) def device_pinners( - self, - enterprise: Optional['Enterprise'] = None, - direction: Optional[str] = None, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + enterprise: Optional['Enterprise'] = None, + direction: Optional[str] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Returns all of the device pins for the given enterprise. @@ -1301,7 +1376,11 @@ def device_pinners( :returns: An iterator of the entries in the device pins. """ - enterprise_id = enterprise.object_id if enterprise is not None else self.get_current_enterprise().id + enterprise_id = ( + enterprise.object_id + if enterprise is not None + else self.get_current_enterprise().id + ) additional_params = {} if direction is not None: additional_params['direction'] = direction @@ -1366,11 +1445,11 @@ def metadata_template_by_id(self, template_id: str) -> 'MetadataTemplate': @api_call def get_metadata_templates( - self, - scope: str = 'enterprise', - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + scope: str = 'enterprise', + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get all metadata templates for a given scope. By default, retrieves all metadata templates for the current @@ -1398,13 +1477,13 @@ def get_metadata_templates( @api_call def create_metadata_template( - self, - display_name: str, - fields: Iterable['MetadataField'], - template_key: str = None, - hidden: bool = False, - scope: str = 'enterprise', - copy_instance_on_item_copy: bool = False + self, + display_name: str, + fields: Iterable['MetadataField'], + template_key: str = None, + hidden: bool = False, + scope: str = 'enterprise', + copy_instance_on_item_copy: bool = False, ) -> 'MetadataTemplate': """ Create a new metadata template. By default, only the display name and fields are required; the template key @@ -1430,7 +1509,7 @@ def create_metadata_template( 'displayName': display_name, 'hidden': hidden, 'fields': [field.json() for field in fields], - 'copyInstanceOnItemCopy': copy_instance_on_item_copy + 'copyInstanceOnItemCopy': copy_instance_on_item_copy, } if template_key is not None: @@ -1459,11 +1538,10 @@ def __create_zip(self, name: str, items: Iterable) -> dict: zip_file_items = [] for item in items: zip_file_items.append({'type': item._item_type, 'id': item.object_id}) - data = { - 'download_file_name': name, - 'items': zip_file_items - } - return self._session.post(url, data=json.dumps(data), skip_retry_codes={202}).json() + data = {'download_file_name': name, 'items': zip_file_items} + return self._session.post( + url, data=json.dumps(data), skip_retry_codes={202} + ).json() @api_call def download_zip(self, name: str, items: Iterable, writeable_stream: IO) -> dict: @@ -1480,8 +1558,12 @@ def download_zip(self, name: str, items: Iterable, writeable_stream: IO) -> dict A status response object """ created_zip = self.__create_zip(name, items) - response = self._session.get(created_zip['download_url'], expect_json_response=False, stream=True) - for chunk in response.network_response.response_as_stream.stream(decode_content=True): + response = self._session.get( + created_zip['download_url'], expect_json_response=False, stream=True + ) + for chunk in response.network_response.response_as_stream.stream( + decode_content=True + ): writeable_stream.write(chunk) status = self._session.get(created_zip['status_url']).json() status.update(created_zip) @@ -1499,7 +1581,9 @@ def folder_lock(self, folder_lock_id: str) -> 'FolderLock': :return: A :class:`FolderLock` object with the given entry ID. """ - return self.translator.get('folder_lock')(session=self._session, object_id=folder_lock_id) + return self.translator.get('folder_lock')( + session=self._session, object_id=folder_lock_id + ) def sign_request(self, sign_request_id: str) -> 'SignRequest': """ @@ -1510,25 +1594,28 @@ def sign_request(self, sign_request_id: str) -> 'SignRequest': :return: A :class:`SignRequest` object with the given file id. """ - return self.translator.get('sign_request')(session=self._session, object_id=sign_request_id) + return self.translator.get('sign_request')( + session=self._session, object_id=sign_request_id + ) # pylint: disable=too-many-branches def __create_sign_request( - self, - signers: Iterable, - files: Optional[Iterable] = None, - parent_folder_id: Optional[str] = None, - prefill_tags: Optional[Iterable] = None, - are_reminders_enabled: Optional[bool] = None, - are_text_signatures_enabled: Optional[bool] = None, - days_valid: Optional[str] = None, - email_message: Optional[Iterable] = None, - email_subject: Optional[str] = None, - external_id: Optional[str] = None, - is_document_preparation_needed: Optional[bool] = None, - redirect_url: Optional[str] = None, - declined_redirect_url: Optional[str] = None, - template_id: Optional[str] = None) -> 'SignRequest': + self, + signers: Iterable, + files: Optional[Iterable] = None, + parent_folder_id: Optional[str] = None, + prefill_tags: Optional[Iterable] = None, + are_reminders_enabled: Optional[bool] = None, + are_text_signatures_enabled: Optional[bool] = None, + days_valid: Optional[str] = None, + email_message: Optional[Iterable] = None, + email_subject: Optional[str] = None, + external_id: Optional[str] = None, + is_document_preparation_needed: Optional[bool] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + template_id: Optional[str] = None, + ) -> 'SignRequest': url = self._session.get_url('sign_requests') body = { @@ -1538,10 +1625,7 @@ def __create_sign_request( if files: body['source_files'] = files if parent_folder_id: - body['parent_folder'] = { - 'id': parent_folder_id, - 'type': 'folder' - } + body['parent_folder'] = {'id': parent_folder_id, 'type': 'folder'} if prefill_tags: body['prefill_tags'] = prefill_tags if are_reminders_enabled: @@ -1575,21 +1659,21 @@ def __create_sign_request( @api_call @deprecated('Use create_sign_request_v2 instead') def create_sign_request( - self, - files: Iterable, - signers: Iterable, - parent_folder_id: str, - prefill_tags: Optional[Iterable] = None, - are_reminders_enabled: Optional[bool] = None, - are_text_signatures_enabled: Optional[bool] = None, - days_valid: Optional[str] = None, - email_message: Optional[Iterable] = None, - email_subject: Optional[str] = None, - external_id: Optional[str] = None, - is_document_preparation_needed: Optional[bool] = None, - redirect_url: Optional[str] = None, - declined_redirect_url: Optional[str] = None, - template_id: Optional[str] = None, + self, + files: Iterable, + signers: Iterable, + parent_folder_id: str, + prefill_tags: Optional[Iterable] = None, + are_reminders_enabled: Optional[bool] = None, + are_text_signatures_enabled: Optional[bool] = None, + days_valid: Optional[str] = None, + email_message: Optional[Iterable] = None, + email_subject: Optional[str] = None, + external_id: Optional[str] = None, + is_document_preparation_needed: Optional[bool] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + template_id: Optional[str] = None, ) -> 'SignRequest': """ Used to create a new sign request. @@ -1632,26 +1716,39 @@ def create_sign_request( A dictionary representing a created SignRequest """ return self.__create_sign_request( - signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message, - email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id) + signers, + files, + parent_folder_id, + prefill_tags, + are_reminders_enabled, + are_text_signatures_enabled, + days_valid, + email_message, + email_subject, + external_id, + is_document_preparation_needed, + redirect_url, + declined_redirect_url, + template_id, + ) @api_call def create_sign_request_v2( - self, - signers: Iterable, - files: Optional[Iterable] = None, - parent_folder_id: Optional[str] = None, - prefill_tags: Optional[Iterable] = None, - are_reminders_enabled: Optional[bool] = None, - are_text_signatures_enabled: Optional[bool] = None, - days_valid: Optional[str] = None, - email_message: Optional[Iterable] = None, - email_subject: Optional[str] = None, - external_id: Optional[str] = None, - is_document_preparation_needed: Optional[bool] = None, - redirect_url: Optional[str] = None, - declined_redirect_url: Optional[str] = None, - template_id: Optional[str] = None, + self, + signers: Iterable, + files: Optional[Iterable] = None, + parent_folder_id: Optional[str] = None, + prefill_tags: Optional[Iterable] = None, + are_reminders_enabled: Optional[bool] = None, + are_text_signatures_enabled: Optional[bool] = None, + days_valid: Optional[str] = None, + email_message: Optional[Iterable] = None, + email_subject: Optional[str] = None, + external_id: Optional[str] = None, + is_document_preparation_needed: Optional[bool] = None, + redirect_url: Optional[str] = None, + declined_redirect_url: Optional[str] = None, + template_id: Optional[str] = None, ) -> 'SignRequest': """ Used to create a new sign request. @@ -1694,15 +1791,28 @@ def create_sign_request_v2( A dictionary representing a created SignRequest """ return self.__create_sign_request( - signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message, - email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id) + signers, + files, + parent_folder_id, + prefill_tags, + are_reminders_enabled, + are_text_signatures_enabled, + days_valid, + email_message, + email_subject, + external_id, + is_document_preparation_needed, + redirect_url, + declined_redirect_url, + template_id, + ) @api_call def get_sign_requests( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Returns all the sign requests. @@ -1734,13 +1844,15 @@ def sign_template(self, sign_template_id: str) -> 'SignTemplate': :return: A :class:`SignTemplate` object with the given sign_template_id. """ - return self.translator.get('sign_template')(session=self._session, object_id=sign_template_id) + return self.translator.get('sign_template')( + session=self._session, object_id=sign_template_id + ) @api_call def get_sign_templates( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, + self, + limit: Optional[int] = None, + marker: Optional[str] = None, ) -> 'BoxObjectCollection': """ Returns all sign templates @@ -1762,8 +1874,8 @@ def get_sign_templates( @api_call def get_sign_template( - self, - sign_template_id: str, + self, + sign_template_id: str, ) -> Any: """ Returns a sign template @@ -1773,7 +1885,9 @@ def get_sign_template( :returns: Sign template """ - response = self._session.get(f"{self._session.get_url('sign_templates')}/{sign_template_id}") + response = self._session.get( + f"{self._session.get_url('sign_templates')}/{sign_template_id}" + ) return self.translator.translate( session=self._session, response_object=response.json(), @@ -1785,7 +1899,7 @@ def send_ai_question( items: Iterable, prompt: str, mode: Optional[str] = None, - ai_agent: Optional[dict] = None + ai_agent: Optional[dict] = None, ) -> Any: """ Sends an AI request to supported LLMs and returns an answer specifically focused on the user's @@ -1809,12 +1923,8 @@ def send_ai_question( """ url = self._session.get_url('ai/ask') if mode is None: - mode = ('single_item_qa' if len(items) == 1 else 'multiple_item_qa') - body = { - 'items': items, - 'prompt': prompt, - 'mode': mode - } + mode = 'single_item_qa' if len(items) == 1 else 'multiple_item_qa' + body = {'items': items, 'prompt': prompt, 'mode': mode} if ai_agent is not None: body['ai_agent'] = ai_agent @@ -1832,7 +1942,7 @@ def send_ai_text_gen( dialogue_history: Iterable, items: Iterable, prompt: str, - ai_agent: Optional[dict] = None + ai_agent: Optional[dict] = None, ): """ Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text. @@ -1851,11 +1961,7 @@ def send_ai_text_gen( A response including the generated text from the LLM. """ url = self._session.get_url('ai/text_gen') - body = { - 'dialogue_history': dialogue_history, - 'items': items, - 'prompt': prompt - } + body = {'dialogue_history': dialogue_history, 'items': items, 'prompt': prompt} if ai_agent is not None: body['ai_agent'] = ai_agent @@ -1869,10 +1975,10 @@ def send_ai_text_gen( @api_call def get_ai_agent_default_config( - self, - mode: str, - language: Optional[str] = None, - model: Optional[str] = None, + self, + mode: str, + language: Optional[str] = None, + model: Optional[str] = None, ): """ Get the AI agent default configuration. diff --git a/boxsdk/client/developer_token_client.py b/boxsdk/client/developer_token_client.py index 6d3d5a861..41c386912 100644 --- a/boxsdk/client/developer_token_client.py +++ b/boxsdk/client/developer_token_client.py @@ -12,5 +12,6 @@ class DeveloperTokenClient(Client): """ Box client subclass which authorizes with a developer token. """ + def __init__(self, oauth: 'OAuth2' = None, session: 'Session' = None): super().__init__(oauth=oauth or DeveloperTokenAuth(), session=session) diff --git a/boxsdk/client/logging_client.py b/boxsdk/client/logging_client.py index bb7859691..da2cf998d 100644 --- a/boxsdk/client/logging_client.py +++ b/boxsdk/client/logging_client.py @@ -8,6 +8,7 @@ class LoggingClient(Client): """ Box client subclass which logs requests and responses. """ + def __init__(self, *args: Any, **kwargs: Any): setup_logging(None) super().__init__(*args, **kwargs) diff --git a/boxsdk/config.py b/boxsdk/config.py index c58290d61..8417f73e5 100644 --- a/boxsdk/config.py +++ b/boxsdk/config.py @@ -5,6 +5,7 @@ class API: """Configuration object containing the URLs for the Box API.""" + BASE_API_URL = 'https://api.box.com/2.0' UPLOAD_URL = 'https://upload.box.com/api/2.0' OAUTH2_API_URL = 'https://api.box.com/oauth2' # @@ -15,10 +16,13 @@ class API: class Client: """Configuration object containing the user agent string.""" + VERSION = version.__version__ USER_AGENT_STRING = f'box-python-sdk-{VERSION}' - BOX_UA_STRING = f'agent=box-python-sdk/{VERSION}; ' \ - f'env=python/{py_version.major}.{py_version.minor}.{py_version.micro}' + BOX_UA_STRING = ( + f'agent=box-python-sdk/{VERSION}; ' + f'env=python/{py_version.major}.{py_version.minor}.{py_version.micro}' + ) class Proxy: diff --git a/boxsdk/exception.py b/boxsdk/exception.py index 31e138cc0..3aab859d0 100644 --- a/boxsdk/exception.py +++ b/boxsdk/exception.py @@ -10,6 +10,7 @@ class BoxException(Exception): """ Base class exception for all errors raised from the SDK. """ + def __str__(self): return self.__class__.__name__ @@ -53,6 +54,7 @@ class BoxAPIException(BoxException): :param network_response: The failed response """ + status: int = attr.ib() code: Optional[str] = attr.ib(default=None) message: Optional[str] = attr.ib(default=None) @@ -64,16 +66,18 @@ class BoxAPIException(BoxException): network_response: 'NetworkResponse' = attr.ib(default=None, repr=False) def __str__(self): - return '\n'.join(( - f'Message: {self.message}', - f'Status: {self.status}', - f'Code: {self.code}', - f'Request ID: {self.request_id}', - f'Headers: {sanitize_dictionary(self.headers)}', - f'URL: {self.url}', - f'Method: {self.method}', - f'Context Info: {self.context_info}', - )) + return '\n'.join( + ( + f'Message: {self.message}', + f'Status: {self.status}', + f'Code: {self.code}', + f'Request ID: {self.request_id}', + f'Headers: {sanitize_dictionary(self.headers)}', + f'URL: {self.url}', + f'Method: {self.method}', + f'Context Info: {self.context_info}', + ) + ) @attr.s(repr=True, slots=True, frozen=True) @@ -94,6 +98,7 @@ class BoxOAuthException(BoxException): :param code: The 'code' field of the failed response """ + status: int = attr.ib() message: str = attr.ib(default=None) url: str = attr.ib(default=None) @@ -108,8 +113,15 @@ def __str__(self): # pylint:enable=no-member else: headers = 'N/A' - return f'\nMessage: {self.message}\nStatus: {self.status}\nURL: {self.url}\nMethod: {self.method}' \ - f'\nHeaders: {headers}' - - -__all__ = ['BoxException', 'BoxAPIException', 'BoxOAuthException', 'BoxNetworkException'] + return ( + f'\nMessage: {self.message}\nStatus: {self.status}\nURL: {self.url}\nMethod: {self.method}' + f'\nHeaders: {headers}' + ) + + +__all__ = [ + 'BoxException', + 'BoxAPIException', + 'BoxOAuthException', + 'BoxNetworkException', +] diff --git a/boxsdk/network/default_network.py b/boxsdk/network/default_network.py index f71c484f2..b8ba28f5e 100644 --- a/boxsdk/network/default_network.py +++ b/boxsdk/network/default_network.py @@ -26,7 +26,9 @@ def __init__(self): self._session = requests.Session() self._logger = getLogger(__name__) - def request(self, method: str, url: str, access_token: str, **kwargs: Any) -> NetworkResponse: + def request( + self, method: str, url: str, access_token: str, **kwargs: Any + ) -> NetworkResponse: """Base class override. Make a network request using a requests.Session. Logs information about an API request and response. @@ -43,13 +45,15 @@ def request(self, method: str, url: str, access_token: str, **kwargs: Any) -> Ne return self.network_response_constructor( request_response=self._session.request(method, url, **kwargs), access_token_used=access_token, - log_response_content=log_response_content + log_response_content=log_response_content, ) except Exception: self._log_exception(method, url, sys.exc_info()) raise - def retry_after(self, delay: float, request_method: Callable, *args: Any, **kwargs: Any) -> Any: + def retry_after( + self, delay: float, request_method: Callable, *args: Any, **kwargs: Any + ) -> Any: """Base class override. Retry after sleeping for delay seconds. """ @@ -78,7 +82,11 @@ def _log_request(self, method: str, url: str, **kwargs: Any) -> None: if self._logger.isEnabledFor(logging.INFO): self._logger.info( self.REQUEST_FORMAT, - {'method': method, 'url': url, 'request_kwargs': pformat(sanitize_dictionary(kwargs))}, + { + 'method': method, + 'url': url, + 'request_kwargs': pformat(sanitize_dictionary(kwargs)), + }, ) def _log_exception(self, method: str, url: str, exc_info: Any) -> None: @@ -92,7 +100,12 @@ def _log_exception(self, method: str, url: str, exc_info: Any) -> None: exc_type, exc_value, _ = exc_info self._logger.warning( self.EXCEPTION_FORMAT, - {'method': method, 'url': url, 'exc_type_name': exc_type.__name__, 'exc_value': exc_value}, + { + 'method': method, + 'url': url, + 'exc_type_name': exc_type.__name__, + 'exc_value': exc_value, + }, ) @@ -142,7 +155,12 @@ class DefaultNetworkResponse(NetworkResponse): ERROR_RESPONSE_FORMAT = f'\x1b[31m{_COMMON_RESPONSE_FORMAT}\x1b[0m' CONTENT_NOT_LOGGED = '' - def __init__(self, request_response: 'Response', access_token_used: str, log_response_content: bool = True): + def __init__( + self, + request_response: 'Response', + access_token_used: str, + log_response_content: bool = True, + ): self._logger = getLogger(__name__) self._request_response = request_response self._access_token_used = access_token_used @@ -216,9 +234,17 @@ def log(self, can_safely_log_content: bool = False) -> None: self._did_log = True if self.ok: - logger_method, logger_level, response_format = self._logger.info, logging.INFO, self.SUCCESSFUL_RESPONSE_FORMAT + logger_method, logger_level, response_format = ( + self._logger.info, + logging.INFO, + self.SUCCESSFUL_RESPONSE_FORMAT, + ) else: - logger_method, logger_level, response_format = self._logger.warning, logging.WARNING, self.ERROR_RESPONSE_FORMAT + logger_method, logger_level, response_format = ( + self._logger.warning, + logging.WARNING, + self.ERROR_RESPONSE_FORMAT, + ) if not self._logger.isEnabledFor(logger_level): return @@ -251,5 +277,7 @@ def log(self, can_safely_log_content: bool = False) -> None: ) def __repr__(self) -> str: - return f'' + return ( + f'' + ) diff --git a/boxsdk/network/network_interface.py b/boxsdk/network/network_interface.py index bc4531060..c865e2daa 100644 --- a/boxsdk/network/network_interface.py +++ b/boxsdk/network/network_interface.py @@ -8,7 +8,9 @@ class Network(ABC): """ @abstractmethod - def request(self, method: str, url: str, access_token: str, **kwargs: Any) -> 'NetworkResponse': + def request( + self, method: str, url: str, access_token: str, **kwargs: Any + ) -> 'NetworkResponse': """ Make a network request to the given url with the given method. @@ -22,7 +24,9 @@ def request(self, method: str, url: str, access_token: str, **kwargs: Any) -> 'N raise NotImplementedError # pragma: no cover @abstractmethod - def retry_after(self, delay: float, request_method: Callable, *args: Any, **kwargs: Any) -> 'NetworkResponse': + def retry_after( + self, delay: float, request_method: Callable, *args: Any, **kwargs: Any + ) -> 'NetworkResponse': """ Make a network request after a given delay. diff --git a/boxsdk/object/__init__.py b/boxsdk/object/__init__.py index d41b3ecd8..391dfb19a 100644 --- a/boxsdk/object/__init__.py +++ b/boxsdk/object/__init__.py @@ -39,5 +39,5 @@ 'upload_session', 'webhook', 'watermark', - 'web_link' + 'web_link', ] diff --git a/boxsdk/object/base_api_json_object.py b/boxsdk/object/base_api_json_object.py index bc9903a2c..005ff72ad 100644 --- a/boxsdk/object/base_api_json_object.py +++ b/boxsdk/object/base_api_json_object.py @@ -51,10 +51,14 @@ def __init__(cls, name, bases, attrs): super().__init__(name, bases, attrs) item_type = attrs.get('_item_type', None) if item_type is not None: - Translator._default_translator.register(item_type, cls) # pylint:disable=protected-access,no-member + Translator._default_translator.register( + item_type, cls + ) # pylint:disable=protected-access,no-member # Some types have - in them instead of _ in the API. if "-" in item_type: - Translator._default_translator.register(item_type.replace("-", "_"), cls) # pylint:disable=protected-access,no-member + Translator._default_translator.register( + item_type.replace("-", "_"), cls + ) # pylint:disable=protected-access,no-member class BaseAPIJSONObject(metaclass=BaseAPIJSONObjectMeta): @@ -119,8 +123,7 @@ def _description(self) -> str: @property def object_type(self) -> str: - """Return the Box type for the object. - """ + """Return the Box type for the object.""" return self._item_type @classmethod @@ -141,7 +144,9 @@ def _untranslate(cls, value: Any) -> Iterable: A dictionary containing the untranslated object. """ if isinstance(value, BaseAPIJSONObject): - return cls._untranslate(value._response_object) # pylint:disable=protected-access + return cls._untranslate( + value._response_object + ) # pylint:disable=protected-access if isinstance(value, dict): return {k: cls._untranslate(v) for (k, v) in value.items()} if isinstance(value, list): diff --git a/boxsdk/object/base_item.py b/boxsdk/object/base_item.py index 05592066a..e3b601bae 100644 --- a/boxsdk/object/base_item.py +++ b/boxsdk/object/base_item.py @@ -16,7 +16,9 @@ class BaseItem(BaseObject): @api_call - def copy(self, *, parent_folder: 'Folder', name: str = None, **_kwargs) -> 'BaseItem': + def copy( + self, *, parent_folder: 'Folder', name: str = None, **_kwargs + ) -> 'BaseItem': """Copy the item to the given folder. :param parent_folder: @@ -26,9 +28,7 @@ def copy(self, *, parent_folder: 'Folder', name: str = None, **_kwargs) -> 'Base """ self.validate_item_id(self._object_id) url = self.get_url('copy') - data = { - 'parent': {'id': parent_folder.object_id} - } + data = {'parent': {'id': parent_folder.object_id}} if name is not None: data['name'] = name box_response = self._session.post(url, data=json.dumps(data)) @@ -48,9 +48,7 @@ def move(self, parent_folder: 'Folder', name: str = None) -> 'BaseItem': :param name: A new name for the item, in case there is already another item in the new parent folder with the same name. """ - data = { - 'parent': {'id': parent_folder.object_id} - } + data = {'parent': {'id': parent_folder.object_id}} if name is not None: data['name'] = name return self.update_info(data=data) @@ -85,7 +83,9 @@ def create_shared_link(self, **kwargs: Any) -> Any: shared_link['access'] = kwargs.get('access') if kwargs.get('unshared_at') is not SDK_VALUE_NOT_SET: - shared_link['unshared_at'] = normalize_date_to_rfc3339_format(kwargs.get('unshared_at')) + shared_link['unshared_at'] = normalize_date_to_rfc3339_format( + kwargs.get('unshared_at') + ) permissions = {} if kwargs.get('allow_download') is not None: @@ -104,7 +104,9 @@ def create_shared_link(self, **kwargs: Any) -> Any: shared_link['vanity_name'] = kwargs.get('vanity_name') data = {'shared_link': shared_link} - update_info_kwargs = {'etag': kwargs.get('etag')} if kwargs.get('etag') is not None else {} + update_info_kwargs = ( + {'etag': kwargs.get('etag')} if kwargs.get('etag') is not None else {} + ) return self.update_info(data=data, **update_info_kwargs) @@ -133,7 +135,9 @@ def remove_shared_link(self, **kwargs: Any) -> bool: Whether or not the update was successful. """ data = {'shared_link': None} - update_info_kwargs = {'etag': kwargs.get('etag')} if kwargs.get('etag') is not None else {} + update_info_kwargs = ( + {'etag': kwargs.get('etag')} if kwargs.get('etag') is not None else {} + ) item = self.update_info(data=data, **update_info_kwargs) return item.shared_link is None # pylint:disable=no-member @@ -148,11 +152,11 @@ def add_to_collection(self, collection: 'Collection') -> 'BaseItem': :return: This item. """ - collections = self.get(fields=['collections']).collections # pylint:disable=no-member + collections = self.get( + fields=['collections'] + ).collections # pylint:disable=no-member collections.append({'id': collection.object_id}) - data = { - 'collections': collections - } + data = {'collections': collections} return self.update_info(data=data) @api_call @@ -165,11 +169,13 @@ def remove_from_collection(self, collection: 'Collection') -> 'BaseItem': :return: This item. """ - collections = self.get(fields=['collections']).collections # pylint:disable=no-member - updated_collections = [c for c in collections if c['id'] != collection.object_id] - data = { - 'collections': updated_collections - } + collections = self.get( + fields=['collections'] + ).collections # pylint:disable=no-member + updated_collections = [ + c for c in collections if c['id'] != collection.object_id + ] + data = {'collections': updated_collections} return self.update_info(data=data) @staticmethod diff --git a/boxsdk/object/base_object.py b/boxsdk/object/base_object.py index 6cd51bb49..4d10cf9ff 100644 --- a/boxsdk/object/base_object.py +++ b/boxsdk/object/base_object.py @@ -12,7 +12,9 @@ class BaseObject(BaseEndpoint, BaseAPIJSONObject): """A Box API endpoint for interacting with a Box object.""" - def __init__(self, session: 'Session', object_id: str, response_object: dict = None): + def __init__( + self, session: 'Session', object_id: str, response_object: dict = None + ): """ :param session: The Box session used to make requests. @@ -54,7 +56,9 @@ def object_id(self) -> str: return self._object_id @api_call - def get(self, *, fields: Iterable[str] = None, headers: dict = None, **_kwargs) -> Any: + def get( + self, *, fields: Iterable[str] = None, headers: dict = None, **_kwargs + ) -> Any: """ Get information about the object, specified by fields. If fields is None, return the default fields. @@ -75,12 +79,12 @@ def get(self, *, fields: Iterable[str] = None, headers: dict = None, **_kwargs) @api_call def update_info( - self, - *, - data: Union[dict, List[dict]], - params: Optional[dict] = None, - headers: Optional[dict] = None, - **kwargs: Any + self, + *, + data: Union[dict, List[dict]], + params: Optional[dict] = None, + headers: Optional[dict] = None, + **kwargs: Any, ) -> Any: """Update information about this object. @@ -108,7 +112,9 @@ def update_info( returned from the endpoint. """ url = self.get_url() - box_response = self._session.put(url, data=json.dumps(data), params=params, headers=headers, **kwargs) + box_response = self._session.put( + url, data=json.dumps(data), params=params, headers=headers, **kwargs + ) if 'expect_json_response' in kwargs and not kwargs['expect_json_response']: return box_response.ok @@ -118,8 +124,14 @@ def update_info( ) @api_call - def delete(self, *, params: Optional[dict] = None, headers: Optional[dict] = None, **_kwargs) -> bool: - """ Delete the object. + def delete( + self, + *, + params: Optional[dict] = None, + headers: Optional[dict] = None, + **_kwargs, + ) -> bool: + """Delete the object. :param params: Additional parameters to send with the request. Can be None @@ -132,7 +144,9 @@ def delete(self, *, params: Optional[dict] = None, headers: Optional[dict] = Non """ url = self.get_url() - box_response = self._session.delete(url, expect_json_response=False, params=params or {}, headers=headers) + box_response = self._session.delete( + url, expect_json_response=False, params=params or {}, headers=headers + ) return box_response.ok def __eq__(self, other: Any) -> bool: diff --git a/boxsdk/object/cloneable.py b/boxsdk/object/cloneable.py index 29711e903..5216c8d2f 100644 --- a/boxsdk/object/cloneable.py +++ b/boxsdk/object/cloneable.py @@ -20,7 +20,9 @@ def as_user(self, user: 'User') -> 'Cloneable': """ return self.clone(self.session.as_user(user)) - def with_shared_link(self, shared_link: str, shared_link_password: str) -> 'Cloneable': + def with_shared_link( + self, shared_link: str, shared_link_password: str + ) -> 'Cloneable': """ Returns a new endpoint object with default headers set up to make requests using the shared link for auth. @@ -29,7 +31,9 @@ def with_shared_link(self, shared_link: str, shared_link_password: str) -> 'Clon :param shared_link_password: The password for the shared link. """ - return self.clone(self.session.with_shared_link(shared_link, shared_link_password)) + return self.clone( + self.session.with_shared_link(shared_link, shared_link_password) + ) def clone(self, session: 'Session' = None) -> NoReturn: """ diff --git a/boxsdk/object/collaboration.py b/boxsdk/object/collaboration.py index 6588e666d..aaf8fbb34 100644 --- a/boxsdk/object/collaboration.py +++ b/boxsdk/object/collaboration.py @@ -7,6 +7,7 @@ class CollaborationRole(TextEnum): """An enum of possible collaboration roles""" + EDITOR = 'editor' VIEWER = 'viewer' @@ -21,6 +22,7 @@ class CollaborationRole(TextEnum): class CollaborationStatus(TextEnum): """An enum of possible statuses of a collaboration""" + PENDING = 'pending' ACCEPTED = 'accepted' REJECTED = 'rejected' @@ -28,16 +30,17 @@ class CollaborationStatus(TextEnum): class Collaboration(BaseObject): """An object that represents a collaboration between a folder and an individual or group""" + _item_type = 'collaboration' @api_call def update_info( - self, - *, - data: dict = None, - role: Optional[CollaborationRole] = None, - status: Optional[CollaborationStatus] = None, - **kwargs: Any + self, + *, + data: dict = None, + role: Optional[CollaborationRole] = None, + status: Optional[CollaborationStatus] = None, + **kwargs: Any ) -> 'BaseObject': """Edit an existing collaboration on Box :param data: diff --git a/boxsdk/object/collaboration_allowlist.py b/boxsdk/object/collaboration_allowlist.py index a5cf31c04..322645d8e 100644 --- a/boxsdk/object/collaboration_allowlist.py +++ b/boxsdk/object/collaboration_allowlist.py @@ -10,13 +10,16 @@ from boxsdk.pagination.box_object_collection import BoxObjectCollection from boxsdk.object.collaboration_allowlist_entry import CollaborationAllowlistEntry from boxsdk.object.user import User - from boxsdk.object.collaboration_allowlist_exempt_target import CollaborationAllowlistExemptTarget + from boxsdk.object.collaboration_allowlist_exempt_target import ( + CollaborationAllowlistExemptTarget, + ) class AllowlistDirection(TextEnum): """ Used to determine the direction of the allowlist. """ + INBOUND = 'inbound' OUTBOUNT = 'outbound' BOTH = 'both' @@ -27,10 +30,10 @@ class CollaborationAllowlist(BaseEndpoint): @api_call def get_entries( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the collaboration allowlist using limit-offset paging. @@ -66,10 +69,7 @@ def add_domain(self, domain: str, direction: str) -> 'CollaborationAllowlistEntr The created allowlist entry for the domain. """ url = self.get_url('collaboration_whitelist_entries') - data = { - 'domain': domain, - 'direction': direction - } + data = {'domain': domain, 'direction': direction} response = self._session.post(url, data=json.dumps(data)).json() return self.translator.translate( session=self._session, @@ -78,10 +78,10 @@ def add_domain(self, domain: str, direction: str) -> 'CollaborationAllowlistEntr @api_call def get_exemptions( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the list of exempted users who are not subject to the collaboration allowlist rules. @@ -115,11 +115,7 @@ def add_exemption(self, user: 'User') -> 'CollaborationAllowlistExemptTarget': The created allowlist exemption. """ url = self.get_url('collaboration_whitelist_exempt_targets') - data = { - 'user': { - 'id': user.object_id # pylint:disable=protected-access - } - } + data = {'user': {'id': user.object_id}} # pylint:disable=protected-access response = self._session.post(url, data=json.dumps(data)).json() return self.translator.translate( session=self._session, diff --git a/boxsdk/object/collaboration_allowlist_entry.py b/boxsdk/object/collaboration_allowlist_entry.py index 71eb45909..7f7673ee1 100644 --- a/boxsdk/object/collaboration_allowlist_entry.py +++ b/boxsdk/object/collaboration_allowlist_entry.py @@ -15,4 +15,6 @@ def get_url(self, *args: Any) -> str: :return: The collaboration allowlist entries endpoint URL. """ - return self._session.get_url('collaboration_whitelist_entries', self._object_id, *args) + return self._session.get_url( + 'collaboration_whitelist_entries', self._object_id, *args + ) diff --git a/boxsdk/object/collection.py b/boxsdk/object/collection.py index 7146f14a3..c353b47a1 100644 --- a/boxsdk/object/collection.py +++ b/boxsdk/object/collection.py @@ -1,7 +1,9 @@ from typing import Optional, Iterable, TYPE_CHECKING from boxsdk.object.base_object import BaseObject -from boxsdk.pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from boxsdk.pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from boxsdk.util.api_call_decorator import api_call if TYPE_CHECKING: @@ -15,10 +17,7 @@ class Collection(BaseObject): @api_call def get_items( - self, - limit: Optional[int] = None, - offset: int = 0, - fields: Iterable[str] = None + self, limit: Optional[int] = None, offset: int = 0, fields: Iterable[str] = None ) -> 'BoxObjectCollection': """ Get the items in a collection using limit-offset paging. diff --git a/boxsdk/object/comment.py b/boxsdk/object/comment.py index 53102f4c3..43b1705ff 100644 --- a/boxsdk/object/comment.py +++ b/boxsdk/object/comment.py @@ -6,14 +6,13 @@ class Comment(BaseObject): """An object that represents a comment on an item""" + _item_type = 'comment' @staticmethod def construct_params_from_message(message: str) -> dict: message_type = 'tagged_message' if '@[' in message else 'message' - return { - message_type: message - } + return {message_type: message} @api_call def reply(self, message: str) -> 'Comment': @@ -25,10 +24,7 @@ def reply(self, message: str) -> 'Comment': """ url = self.get_type_url() data = self.construct_params_from_message(message) - data['item'] = { - 'type': 'comment', - 'id': self.object_id - } + data['item'] = {'type': 'comment', 'id': self.object_id} box_response = self._session.post(url, data=json.dumps(data)) response = box_response.json() return self.translator.translate( diff --git a/boxsdk/object/enterprise.py b/boxsdk/object/enterprise.py index f0717a509..949fd62df 100644 --- a/boxsdk/object/enterprise.py +++ b/boxsdk/object/enterprise.py @@ -9,8 +9,8 @@ class Enterprise(BaseObject): - """Represents a single enterprise.""" + _item_type = 'enterprise' @api_call diff --git a/boxsdk/object/events.py b/boxsdk/object/events.py index 302097703..3d00d55e8 100644 --- a/boxsdk/object/events.py +++ b/boxsdk/object/events.py @@ -35,6 +35,7 @@ class UserEventsStreamType(EventsStreamType): """ + ALL = 'all' CHANGES = 'changes' SYNC = 'sync' @@ -51,8 +52,11 @@ class EnterpriseEventsStreamType(EventsStreamType): """ + ADMIN_LOGS = 'admin_logs' ADMIN_LOGS_STREAMING = 'admin_logs_streaming' + + # pylint:enable=too-many-ancestors @@ -65,10 +69,10 @@ def get_url(self, *args: Any) -> str: @api_call def get_events( - self, - limit: int = 100, - stream_position: Union[str, int] = 0, - stream_type: EventsStreamType = UserEventsStreamType.ALL + self, + limit: int = 100, + stream_position: Union[str, int] = 0, + stream_type: EventsStreamType = UserEventsStreamType.ALL, ) -> dict: """ Get Box events from a given stream position for a given stream type. @@ -96,12 +100,12 @@ def get_events( @api_call def get_admin_events( - self, - limit: Optional[int] = None, - stream_position: Union[str, int] = 0, - created_after: Union[datetime, str] = None, - created_before: Union[datetime, str] = None, - event_types: Iterable[str] = None + self, + limit: Optional[int] = None, + stream_position: Union[str, int] = 0, + created_after: Union[datetime, str] = None, + created_before: Union[datetime, str] = None, + event_types: Iterable[str] = None, ) -> dict: """ Get Box Admin events from a datetime, to a datetime, or between datetimes with a given event type for a enterprise @@ -138,10 +142,10 @@ def get_admin_events( @api_call def get_admin_events_streaming( - self, - limit: Optional[int] = None, - stream_position: Union[str, int] = 0, - event_types: Iterable[str] = None + self, + limit: Optional[int] = None, + stream_position: Union[str, int] = 0, + event_types: Iterable[str] = None, ) -> dict: """ Get Box Admin events with a given event type for a enterprise stream type. Used for live monitoring (up to two weeks). @@ -172,7 +176,9 @@ def get_admin_events_streaming( return self.translator.translate(self._session, response_object=response) @api_call - def get_latest_stream_position(self, stream_type: UserEventsStreamType = UserEventsStreamType.ALL) -> int: + def get_latest_stream_position( + self, stream_type: UserEventsStreamType = UserEventsStreamType.ALL + ) -> int: """ Get the latest stream position. The return value can be used with :meth:`get_events` or :meth:`generate_events_with_long_polling`. @@ -186,12 +192,14 @@ def get_latest_stream_position(self, stream_type: UserEventsStreamType = UserEve :returns: The latest stream position. """ - return self.get_events(limit=0, stream_position='now', stream_type=stream_type)['next_stream_position'] + return self.get_events(limit=0, stream_position='now', stream_type=stream_type)[ + 'next_stream_position' + ] def _get_all_events_since( - self, - stream_position: Union[str, int], - stream_type: EventsStreamType = UserEventsStreamType.ALL + self, + stream_position: Union[str, int], + stream_type: EventsStreamType = UserEventsStreamType.ALL, ) -> Generator[tuple, None, None]: """ :param stream_position: @@ -202,7 +210,9 @@ def _get_all_events_since( """ next_stream_position = stream_position while True: - events = self.get_events(stream_position=next_stream_position, limit=100, stream_type=stream_type) + events = self.get_events( + stream_position=next_stream_position, limit=100, stream_type=stream_type + ) next_stream_position = events['next_stream_position'] events = events['entries'] if not events: @@ -213,7 +223,9 @@ def _get_all_events_since( return @api_call - def long_poll(self, options: dict, stream_position: Union[str, int]) -> 'BoxResponse': + def long_poll( + self, options: dict, stream_position: Union[str, int] + ) -> 'BoxResponse': """ Set up a long poll connection at the specified url. @@ -230,15 +242,15 @@ def long_poll(self, options: dict, stream_position: Union[str, int]) -> 'BoxResp long_poll_response = self._session.get( url, timeout=options['retry_timeout'], - params={'stream_position': stream_position} + params={'stream_position': stream_position}, ) return long_poll_response @api_call def generate_events_with_long_polling( - self, - stream_position: Union[str, int] = None, - stream_type: UserEventsStreamType = UserEventsStreamType.ALL + self, + stream_position: Union[str, int] = None, + stream_type: UserEventsStreamType = UserEventsStreamType.ALL, ) -> Generator['Event', None, None]: """ Subscribe to events from the given stream position. @@ -256,7 +268,11 @@ def generate_events_with_long_polling( Events corresponding to changes on Box in realtime, as they come in. """ event_ids = LRUCache() - stream_position = stream_position if stream_position is not None else self.get_latest_stream_position(stream_type=stream_type) + stream_position = ( + stream_position + if stream_position is not None + else self.get_latest_stream_position(stream_type=stream_type) + ) while True: options = self.get_long_poll_options(stream_type=stream_type) while True: @@ -268,7 +284,9 @@ def generate_events_with_long_polling( message = long_poll_response.json()['message'] if message == 'new_change': next_stream_position = stream_position - for event, next_stream_position in self._get_all_events_since(stream_position, stream_type=stream_type): + for event, next_stream_position in self._get_all_events_since( + stream_position, stream_type=stream_type + ): try: event_ids.get(event['event_id']) except KeyError: @@ -281,7 +299,9 @@ def generate_events_with_long_polling( break @api_call - def get_long_poll_options(self, stream_type: EventsStreamType = UserEventsStreamType.ALL) -> dict: + def get_long_poll_options( + self, stream_type: EventsStreamType = UserEventsStreamType.ALL + ) -> dict: """ Get the url and retry timeout for setting up a long polling connection. diff --git a/boxsdk/object/file.py b/boxsdk/object/file.py index c9128a056..c7768312f 100644 --- a/boxsdk/object/file.py +++ b/boxsdk/object/file.py @@ -10,7 +10,9 @@ from ..util.default_arg_value import SDK_VALUE_NOT_SET from ..util.deprecation_decorator import deprecated from ..pagination.marker_based_object_collection import MarkerBasedObjectCollection -from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from ..pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) if TYPE_CHECKING: from boxsdk.object.upload_session import UploadSession @@ -50,7 +52,10 @@ def preflight_check(self, size: int, name: Optional[str] = None) -> Optional[str @api_call def create_upload_session( - self, file_size: int, file_name: Optional[str] = None, use_upload_session_urls: bool = True + self, + file_size: int, + file_name: Optional[str] = None, + use_upload_session_urls: bool = True, ) -> 'UploadSession': """ Create a new chunked upload session for uploading a new version of the file. @@ -73,7 +78,9 @@ def create_upload_session( } if file_name is not None: body_params['file_name'] = file_name - url = self.get_url('upload_sessions').replace(self.session.api_config.BASE_API_URL, self.session.api_config.UPLOAD_URL) + url = self.get_url('upload_sessions').replace( + self.session.api_config.BASE_API_URL, self.session.api_config.UPLOAD_URL + ) response = self._session.post(url, data=json.dumps(body_params)).json() upload_session = self.translator.translate( session=self._session, @@ -85,7 +92,10 @@ def create_upload_session( @api_call def get_chunked_uploader( - self, file_path: str, rename_file: bool = False, use_upload_session_urls: bool = True + self, + file_path: str, + rename_file: bool = False, + use_upload_session_urls: bool = True, ) -> 'ChunkedUploader': # pylint: disable=consider-using-with """ @@ -106,8 +116,12 @@ def get_chunked_uploader( total_size = os.stat(file_path).st_size content_stream = open(file_path, 'rb') file_name = os.path.basename(file_path) if rename_file else None - upload_session = self.create_upload_session(total_size, file_name, use_upload_session_urls) - return upload_session.get_chunked_uploader_for_stream(content_stream, total_size) + upload_session = self.create_upload_session( + total_size, file_name, use_upload_session_urls + ) + return upload_session.get_chunked_uploader_for_stream( + content_stream, total_size + ) def _get_accelerator_upload_url_for_update(self) -> Optional[str]: """ @@ -137,7 +151,11 @@ def _construct_range_header(boundaries: Union[Tuple[int], Tuple[int, int]]) -> s raise ValueError('Expected a 1-tuple or 2-tuple for byte range') @api_call - def content(self, file_version: Optional['FileVersion'] = None, byte_range: Tuple[int, int] = None) -> bytes: + def content( + self, + file_version: Optional['FileVersion'] = None, + byte_range: Tuple[int, int] = None, + ) -> bytes: """ Get the content of a file on Box. @@ -149,17 +167,25 @@ def content(self, file_version: Optional['FileVersion'] = None, byte_range: Tupl File content as bytes. """ url = self.get_url('content') - params = {'version': file_version.object_id} if file_version is not None else None - headers = {'Range': self._construct_range_header(byte_range)} if byte_range is not None else None - box_response = self._session.get(url, expect_json_response=False, params=params, headers=headers) + params = ( + {'version': file_version.object_id} if file_version is not None else None + ) + headers = ( + {'Range': self._construct_range_header(byte_range)} + if byte_range is not None + else None + ) + box_response = self._session.get( + url, expect_json_response=False, params=params, headers=headers + ) return box_response.content @api_call def download_to( - self, - writeable_stream: IO[bytes], - file_version: Optional['FileVersion'] = None, - byte_range: Tuple[int, int] = None + self, + writeable_stream: IO[bytes], + file_version: Optional['FileVersion'] = None, + byte_range: Tuple[int, int] = None, ) -> None: """ Download the file; write it to the given stream. @@ -172,10 +198,20 @@ def download_to( A tuple of inclusive byte offsets to download, e.g. (100, 199) to download the second 100 bytes of a file """ url = self.get_url('content') - params = {'version': file_version.object_id} if file_version is not None else None - headers = {'Range': self._construct_range_header(byte_range)} if byte_range is not None else None - box_response = self._session.get(url, expect_json_response=False, stream=True, params=params, headers=headers) - for chunk in box_response.network_response.response_as_stream.stream(decode_content=True): + params = ( + {'version': file_version.object_id} if file_version is not None else None + ) + headers = ( + {'Range': self._construct_range_header(byte_range)} + if byte_range is not None + else None + ) + box_response = self._session.get( + url, expect_json_response=False, stream=True, params=params, headers=headers + ) + for chunk in box_response.network_response.response_as_stream.stream( + decode_content=True + ): writeable_stream.write(chunk) @api_call @@ -188,7 +224,9 @@ def get_download_url(self, file_version: Optional['FileVersion'] = None) -> str: :return: Url to download the file """ url = self.get_url('content') - params = {'version': file_version.object_id} if file_version is not None else None + params = ( + {'version': file_version.object_id} if file_version is not None else None + ) box_response = self._session.get( url, params=params, @@ -210,16 +248,16 @@ def get_download_url(self, file_version: Optional['FileVersion'] = None) -> str: @api_call def update_contents_with_stream( - self, - file_stream: IO[bytes], - etag: Optional[str] = None, - preflight_check: bool = False, - preflight_expected_size: int = 0, - upload_using_accelerator: bool = False, - file_name: Optional[str] = None, - content_modified_at: Union[datetime, str] = None, - additional_attributes: Optional[dict] = None, - sha1: Optional[str] = None, + self, + file_stream: IO[bytes], + etag: Optional[str] = None, + preflight_check: bool = False, + preflight_expected_size: int = 0, + upload_using_accelerator: bool = False, + file_name: Optional[str] = None, + content_modified_at: Union[datetime, str] = None, + additional_attributes: Optional[dict] = None, + sha1: Optional[str] = None, ) -> 'File': """ Upload a new version of a file, taking the contents from the given file stream. @@ -272,7 +310,9 @@ def update_contents_with_stream( attributes = { 'name': file_name, - 'content_modified_at': normalize_date_to_rfc3339_format(content_modified_at), + 'content_modified_at': normalize_date_to_rfc3339_format( + content_modified_at + ), } if additional_attributes: attributes.update(additional_attributes) @@ -303,16 +343,16 @@ def update_contents_with_stream( @api_call def update_contents( - self, - file_path: str, - etag: Optional[str] = None, - preflight_check: bool = False, - preflight_expected_size: int = 0, - upload_using_accelerator: bool = False, - file_name: Optional[str] = None, - content_modified_at: Union[datetime, str] = None, - additional_attributes: Optional[dict] = None, - sha1: Optional[str] = None, + self, + file_path: str, + etag: Optional[str] = None, + preflight_check: bool = False, + preflight_expected_size: int = 0, + upload_using_accelerator: bool = False, + file_name: Optional[str] = None, + content_modified_at: Union[datetime, str] = None, + additional_attributes: Optional[dict] = None, + sha1: Optional[str] = None, ) -> 'File': """Upload a new version of a file. The contents are taken from the given file path. @@ -362,7 +402,9 @@ def update_contents( ) @api_call - def lock(self, prevent_download: bool = False, expire_time: Union[datetime, str] = None) -> 'File': + def lock( + self, prevent_download: bool = False, expire_time: Union[datetime, str] = None + ) -> 'File': """ Lock a file, preventing others from modifying (or possibly even downloading) it. @@ -398,17 +440,17 @@ def unlock(self) -> 'File': @api_call def create_shared_link( - self, - *, - access: Optional[str] = None, - etag: Optional[str] = None, - unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, - allow_download: Optional[bool] = None, - allow_preview: Optional[bool] = None, - allow_edit: Optional[bool] = None, - password: Optional[str] = None, - vanity_name: Optional[str] = None, - **kwargs: Any + self, + *, + access: Optional[str] = None, + etag: Optional[str] = None, + unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, + allow_download: Optional[bool] = None, + allow_preview: Optional[bool] = None, + allow_edit: Optional[bool] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + **kwargs: Any, ) -> 'File': """ Baseclass override. @@ -454,22 +496,22 @@ def create_shared_link( allow_preview=allow_preview, allow_edit=allow_edit, password=password, - vanity_name=vanity_name + vanity_name=vanity_name, ) @api_call def get_shared_link( - self, - *, - access: Optional[str] = None, - etag: Optional[str] = None, - unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, - allow_download: Optional[bool] = None, - allow_preview: Optional[bool] = None, - allow_edit: Optional[bool] = None, - password: Optional[str] = None, - vanity_name: Optional[str] = None, - **kwargs: Any + self, + *, + access: Optional[str] = None, + etag: Optional[str] = None, + unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, + allow_download: Optional[bool] = None, + allow_preview: Optional[bool] = None, + allow_edit: Optional[bool] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + **kwargs: Any, ) -> 'str': """ Baseclass override. @@ -514,18 +556,18 @@ def get_shared_link( allow_preview=allow_preview, allow_edit=allow_edit, password=password, - vanity_name=vanity_name + vanity_name=vanity_name, ) @api_call def get_shared_link_download_url( - self, - access: Optional[str] = None, - etag: Optional[str] = None, - unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, - allow_preview: Optional[bool] = None, - password: Optional[str] = None, - vanity_name: Optional[str] = None + self, + access: Optional[str] = None, + etag: Optional[str] = None, + unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, + allow_preview: Optional[bool] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, ) -> str: """ Get a shared link download url for the file with the given access permissions. @@ -560,16 +602,13 @@ def get_shared_link_download_url( unshared_at=unshared_at, allow_preview=allow_preview, password=password, - vanity_name=vanity_name + vanity_name=vanity_name, ) return item.shared_link['download_url'] # pylint:disable=no-member @api_call def get_comments( - self, - limit: Optional[int] = None, - offset: int = 0, - fields: Iterable[str] = None + self, limit: Optional[int] = None, offset: int = 0, fields: Iterable[str] = None ) -> 'BoxObjectCollection': """ Get the comments on the file. @@ -604,10 +643,7 @@ def add_comment(self, message: str) -> 'Comment': url = self._session.get_url('comments') comment_class = self._session.translator.get('comment') data = comment_class.construct_params_from_message(message) - data['item'] = { - 'type': 'file', - 'id': self.object_id - } + data['item'] = {'type': 'file', 'id': self.object_id} box_response = self._session.post(url, data=json.dumps(data)) response = box_response.json() return self._session.translator.translate( @@ -617,11 +653,11 @@ def add_comment(self, message: str) -> 'Comment': @api_call def create_task( - self, - message: Optional[str] = None, - due_at: Union[datetime, str] = None, - action: str = 'review', - completion_rule: Optional[str] = None + self, + message: Optional[str] = None, + due_at: Union[datetime, str] = None, + action: str = 'review', + completion_rule: Optional[str] = None, ) -> 'Task': """ Create a task on the given file. @@ -643,10 +679,7 @@ def create_task( """ url = self._session.get_url('tasks') task_attributes = { - 'item': { - 'type': 'file', - 'id': self.object_id - }, + 'item': {'type': 'file', 'id': self.object_id}, 'action': action, } if message is not None: @@ -683,10 +716,10 @@ def get_tasks(self, fields: Iterable[str] = None) -> 'BoxObjectCollection': @api_call def get_previous_versions( - self, - limit: Optional[int] = None, - offset: int = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + offset: int = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get previous versions of the file. @@ -732,7 +765,9 @@ def promote_version(self, file_version: 'FileVersion') -> 'FileVersion': ) @api_call - def delete_version(self, file_version: 'FileVersion', etag: Optional[str] = None) -> bool: + def delete_version( + self, file_version: 'FileVersion', etag: Optional[str] = None + ) -> bool: """ Delete a specific version of a file. @@ -745,7 +780,9 @@ def delete_version(self, file_version: 'FileVersion', etag: Optional[str] = None """ url = self.get_url('versions', file_version.object_id) headers = {'If-Match': etag} if etag is not None else None - response = self._session.delete(url, expect_json_response=False, headers=headers) + response = self._session.delete( + url, expect_json_response=False, headers=headers + ) return response.ok @api_call @@ -780,12 +817,12 @@ def get_representation_info(self, rep_hints: Optional[str] = None) -> List[dict] @deprecated('Use get_thumbnail_representation') @api_call def get_thumbnail( - self, - extension: str = 'png', - min_width: Optional[int] = None, - min_height: Optional[int] = None, - max_width: Optional[int] = None, - max_height: Optional[int] = None + self, + extension: str = 'png', + min_width: Optional[int] = None, + min_height: Optional[int] = None, + max_width: Optional[int] = None, + max_height: Optional[int] = None, ) -> bytes: """ Retrieve a thumbnail image for the file. @@ -818,7 +855,9 @@ def get_thumbnail( return response.content @api_call - def get_thumbnail_representation(self, dimensions: str, extension: str = 'png') -> bytes: + def get_thumbnail_representation( + self, dimensions: str, extension: str = 'png' + ) -> bytes: """ Retrieve a thumbnail image for the file. @@ -833,7 +872,10 @@ def get_thumbnail_representation(self, dimensions: str, extension: str = 'png') representations = self.get_representation_info(rep_hints) if representations: representation = representations[0] - if representation['status'].get('code') in ('error_conversion_failed', 'error_password_protected'): + if representation['status'].get('code') in ( + 'error_conversion_failed', + 'error_password_protected', + ): return b'' url = representation['content']['url_template'] url = url.replace('{+asset_path}', '') @@ -843,12 +885,12 @@ def get_thumbnail_representation(self, dimensions: str, extension: str = 'png') @api_call def copy( - self, - *, - parent_folder: 'Folder', - name: Optional[str] = None, - file_version: 'FileVersion' = None, - **_kwargs + self, + *, + parent_folder: 'Folder', + name: Optional[str] = None, + file_version: 'FileVersion' = None, + **_kwargs, ) -> 'File': # pylint: disable=arguments-differ """Copy the item to the given folder. @@ -864,9 +906,7 @@ def copy( """ # pylint: disable=arguments-differ url = self.get_url('copy') - data = { - 'parent': {'id': parent_folder.object_id} - } + data = {'parent': {'id': parent_folder.object_id}} if name is not None: data['name'] = name if file_version is not None: diff --git a/boxsdk/object/file_request.py b/boxsdk/object/file_request.py index 1ae4c90ff..3b3112d29 100644 --- a/boxsdk/object/file_request.py +++ b/boxsdk/object/file_request.py @@ -14,26 +14,28 @@ class StatusState(TextEnum): """An enum of possible status states""" + ACTIVE = 'active' INACTIVE = 'inactive' class FileRequest(BaseObject): """Represents the file request.""" + _item_type = 'file_request' @api_call def copy( - self, - *, - folder: 'Folder', - description: Optional[str] = None, - expires_at: Union[datetime, str] = None, - require_description: Optional[bool] = None, - require_email: Optional[bool] = None, - status: Optional[str] = None, - title: Optional[str] = None, - **_kwargs + self, + *, + folder: 'Folder', + description: Optional[str] = None, + expires_at: Union[datetime, str] = None, + require_description: Optional[bool] = None, + require_email: Optional[bool] = None, + status: Optional[str] = None, + title: Optional[str] = None, + **_kwargs ) -> 'FileRequest': """Copy an existing file request already present on one folder, and applies it to another folder. diff --git a/boxsdk/object/file_version.py b/boxsdk/object/file_version.py index 7299d99df..6811f586e 100644 --- a/boxsdk/object/file_version.py +++ b/boxsdk/object/file_version.py @@ -3,4 +3,5 @@ class FileVersion(BaseObject): """Represents a Box file version.""" + _item_type = 'file_version' diff --git a/boxsdk/object/file_version_retention.py b/boxsdk/object/file_version_retention.py index 08f0473fe..f22b3ad41 100644 --- a/boxsdk/object/file_version_retention.py +++ b/boxsdk/object/file_version_retention.py @@ -3,4 +3,5 @@ class FileVersionRetention(BaseObject): """Represents a Box file version retention.""" + _item_type = 'file_version_retention' diff --git a/boxsdk/object/folder.py b/boxsdk/object/folder.py index d20f5b008..099732464 100644 --- a/boxsdk/object/folder.py +++ b/boxsdk/object/folder.py @@ -6,7 +6,9 @@ from boxsdk.object.group import Group from boxsdk.object.item import Item from boxsdk.object.user import User -from boxsdk.pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from boxsdk.pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from boxsdk.pagination.marker_based_object_collection import MarkerBasedObjectCollection from boxsdk.util.api_call_decorator import api_call from boxsdk.util.datetime_formatter import normalize_date_to_rfc3339_format @@ -32,6 +34,7 @@ class FolderSyncState(TextEnum): The value of the ``sync_state`` attribute determines whether the folder will be synced by sync clients. """ + IS_SYNCED = 'synced' NOT_SYNCED = 'not_synced' PARTIALLY_SYNCED = 'partially_synced' @@ -39,6 +42,7 @@ class FolderSyncState(TextEnum): class _CollaborationType(TextEnum): """The type of a collaboration""" + USER = 'user' GROUP = 'group' @@ -56,7 +60,9 @@ def __init__(self, collaborator: Any): else: raise TypeError('Collaborator must be User, Group, or unicode string') - def _setup(self, user: User = None, group: Group = None, email_address: str = None) -> None: + def _setup( + self, user: User = None, group: Group = None, email_address: str = None + ) -> None: """ :param user: The Box user if applicable @@ -115,7 +121,9 @@ def preflight_check(self, size: int, name: str) -> Optional[str]: ) @api_call - def create_upload_session(self, file_size: int, file_name: str, use_upload_session_urls: bool = True) -> 'UploadSession': + def create_upload_session( + self, file_size: int, file_name: str, use_upload_session_urls: bool = True + ) -> 'UploadSession': """ Creates a new chunked upload session for upload a new file. @@ -148,7 +156,10 @@ def create_upload_session(self, file_size: int, file_name: str, use_upload_sessi @api_call def get_chunked_uploader( - self, file_path: str, file_name: Optional[str] = None, use_upload_session_urls: bool = True + self, + file_path: str, + file_name: Optional[str] = None, + use_upload_session_urls: bool = True, ) -> 'ChunkedUploader': # pylint: disable=consider-using-with """ @@ -172,8 +183,12 @@ def get_chunked_uploader( content_stream = open(file_path, 'rb') try: - upload_session = self.create_upload_session(total_size, upload_file_name, use_upload_session_urls) - return upload_session.get_chunked_uploader_for_stream(content_stream, total_size) + upload_session = self.create_upload_session( + total_size, upload_file_name, use_upload_session_urls + ) + return upload_session.get_chunked_uploader_for_stream( + content_stream, total_size + ) except Exception: content_stream.close() raise @@ -189,14 +204,14 @@ def _get_accelerator_upload_url_fow_new_uploads(self) -> Optional[str]: @api_call def get_items( - self, - limit: Optional[int] = None, - offset: int = 0, - marker: Optional[str] = None, - use_marker: bool = False, - sort: Optional[str] = None, - direction: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + offset: int = 0, + marker: Optional[str] = None, + use_marker: bool = False, + sort: Optional[str] = None, + direction: Optional[str] = None, + fields: Iterable[str] = None, ) -> Iterable[Item]: """ Get the items in a folder. @@ -251,19 +266,19 @@ def get_items( @api_call def upload_stream( - self, - file_stream: IO[bytes], - file_name: str, - file_description: Optional[str] = None, - preflight_check: bool = False, - preflight_expected_size: int = 0, - upload_using_accelerator: bool = False, - content_created_at: Union[datetime, str] = None, - content_modified_at: Union[datetime, str] = None, - additional_attributes: Optional[dict] = None, - sha1: Optional[str] = None, - etag: Optional[str] = None, - stream_file_content: bool = True, + self, + file_stream: IO[bytes], + file_name: str, + file_description: Optional[str] = None, + preflight_check: bool = False, + preflight_expected_size: int = 0, + upload_using_accelerator: bool = False, + content_created_at: Union[datetime, str] = None, + content_modified_at: Union[datetime, str] = None, + additional_attributes: Optional[dict] = None, + sha1: Optional[str] = None, + etag: Optional[str] = None, + stream_file_content: bool = True, ) -> 'File': """ Upload a file to the folder. @@ -308,7 +323,9 @@ def upload_stream( accelerator_upload_url = None if preflight_check: # Preflight check does double duty, returning the accelerator URL if one is available in the response. - accelerator_upload_url = self.preflight_check(size=preflight_expected_size, name=file_name) + accelerator_upload_url = self.preflight_check( + size=preflight_expected_size, name=file_name + ) elif upload_using_accelerator: accelerator_upload_url = self._get_accelerator_upload_url_fow_new_uploads() @@ -321,7 +338,9 @@ def upload_stream( 'parent': {'id': self._object_id}, 'description': file_description, 'content_created_at': normalize_date_to_rfc3339_format(content_created_at), - 'content_modified_at': normalize_date_to_rfc3339_format(content_modified_at), + 'content_modified_at': normalize_date_to_rfc3339_format( + content_modified_at + ), } if additional_attributes: attributes.update(additional_attributes) @@ -339,7 +358,12 @@ def upload_stream( if not headers: headers = None file_response = self._session.post( - url, data=data, files=files, expect_json_response=False, headers=headers, stream_file_content=stream_file_content, + url, + data=data, + files=files, + expect_json_response=False, + headers=headers, + stream_file_content=stream_file_content, ).json() if 'entries' in file_response: file_response = file_response['entries'][0] @@ -350,19 +374,19 @@ def upload_stream( @api_call def upload( - self, - file_path: str = None, - file_name: str = None, - file_description: Optional[str] = None, - preflight_check: bool = False, - preflight_expected_size: int = 0, - upload_using_accelerator: bool = False, - content_created_at: Union[datetime, str] = None, - content_modified_at: Union[datetime, str] = None, - additional_attributes: Optional[dict] = None, - sha1: Optional[str] = None, - etag: Optional[str] = None, - stream_file_content: bool = True, + self, + file_path: str = None, + file_name: str = None, + file_description: Optional[str] = None, + preflight_check: bool = False, + preflight_expected_size: int = 0, + upload_using_accelerator: bool = False, + content_created_at: Union[datetime, str] = None, + content_modified_at: Union[datetime, str] = None, + additional_attributes: Optional[dict] = None, + sha1: Optional[str] = None, + etag: Optional[str] = None, + stream_file_content: bool = True, ) -> 'File': """ Upload a file to the folder. @@ -436,7 +460,7 @@ def create_subfolder(self, name: str) -> 'Folder': 'name': name, 'parent': { 'id': self._object_id, - } + }, } box_response = self._session.post(url, data=json.dumps(data)) response = box_response.json() @@ -464,16 +488,16 @@ def update_sync_state(self, sync_state: FolderSyncState) -> 'Folder': @api_call def create_shared_link( - self, - *, - access: Optional[str] = None, - etag: Optional[str] = None, - unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, - allow_download: Optional[bool] = None, - allow_preview: Optional[bool] = None, - password: Optional[str] = None, - vanity_name: Optional[str] = None, - **kwargs: Any + self, + *, + access: Optional[str] = None, + etag: Optional[str] = None, + unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, + allow_download: Optional[bool] = None, + allow_preview: Optional[bool] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + **kwargs: Any, ) -> 'Folder': """ Baseclass override. @@ -515,21 +539,21 @@ def create_shared_link( allow_download=allow_download, allow_preview=allow_preview, password=password, - vanity_name=vanity_name + vanity_name=vanity_name, ) @api_call def get_shared_link( - self, - *, - access: Optional[str] = None, - etag: Optional[str] = None, - unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, - allow_download: Optional[bool] = None, - allow_preview: Optional[bool] = None, - password: Optional[str] = None, - vanity_name: Optional[str] = None, - **kwargs: Any + self, + *, + access: Optional[str] = None, + etag: Optional[str] = None, + unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, + allow_download: Optional[bool] = None, + allow_preview: Optional[bool] = None, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + **kwargs: Any, ) -> 'str': """ Baseclass override. @@ -570,16 +594,16 @@ def get_shared_link( allow_download=allow_download, allow_preview=allow_preview, password=password, - vanity_name=vanity_name + vanity_name=vanity_name, ) @api_call def add_collaborator( - self, - collaborator: Union[User, Group, str], - role: 'CollaborationRole', - notify: bool = False, - can_view_path: bool = False + self, + collaborator: Union[User, Group, str], + role: 'CollaborationRole', + notify: bool = False, + can_view_path: bool = False, ) -> 'Collaboration': """Add a collaborator to the folder @@ -612,7 +636,9 @@ def add_collaborator( body_params['can_view_path'] = True data = json.dumps(body_params) params = {'notify': notify} - box_response = self._session.post(url, expect_json_response=True, data=data, params=params) + box_response = self._session.post( + url, expect_json_response=True, data=data, params=params + ) collaboration_response = box_response.json() return self.translator.translate( session=self._session, @@ -621,10 +647,10 @@ def add_collaborator( @api_call def create_web_link( - self, - target_url: str, - name: Optional[str] = None, - description: Optional[str] = None + self, + target_url: str, + name: Optional[str] = None, + description: Optional[str] = None, ) -> 'WebLink': """ Create a WebLink with a given url. @@ -639,29 +665,19 @@ def create_web_link( A :class:`WebLink` object. """ url = self._session.get_url('web_links') - web_link_attributes = { - 'url': target_url, - 'parent': { - 'id': self.object_id - } - } + web_link_attributes = {'url': target_url, 'parent': {'id': self.object_id}} if name is not None: web_link_attributes['name'] = name if description is not None: web_link_attributes['description'] = description response = self._session.post(url, data=json.dumps(web_link_attributes)).json() return self.translator.translate( - session=self._session, - response_object=response + session=self._session, response_object=response ) @api_call def delete( - self, - *, - recursive: bool = True, - etag: Optional[str] = None, - **kwargs + self, *, recursive: bool = True, etag: Optional[str] = None, **kwargs ) -> bool: """Base class override. Delete the folder. @@ -678,11 +694,11 @@ def delete( @api_call def get_metadata_cascade_policies( - self, - owner_enterprise: 'Enterprise' = None, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + owner_enterprise: 'Enterprise' = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the metadata cascade policies current applied to the folder. @@ -716,7 +732,9 @@ def get_metadata_cascade_policies( ) @api_call - def cascade_metadata(self, metadata_template: 'MetadataTemplate') -> 'MetadataCascadePolicy': + def cascade_metadata( + self, metadata_template: 'MetadataTemplate' + ) -> 'MetadataCascadePolicy': """ Create a metadata cascade policy to apply the metadata instance values on the folder for the given metadata template to all files within the folder. @@ -748,14 +766,8 @@ def create_lock(self) -> 'FolderLock': url = self._session.get_url('folder_locks') body = { - 'folder': { - 'type': 'folder', - 'id': self.object_id - }, - 'locked_operations': { - 'move': True, - 'delete': True - } + 'folder': {'type': 'folder', 'id': self.object_id}, + 'locked_operations': {'move': True, 'delete': True}, } response = self._session.post(url, data=json.dumps(body)).json() diff --git a/boxsdk/object/group.py b/boxsdk/object/group.py index 93d3d689a..a4f13e96f 100644 --- a/boxsdk/object/group.py +++ b/boxsdk/object/group.py @@ -3,7 +3,9 @@ from boxsdk.util.text_enum import TextEnum from .base_object import BaseObject -from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from ..pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from ..util.api_call_decorator import api_call from ..util.default_arg_value import SDK_VALUE_NOT_SET @@ -15,6 +17,7 @@ class GroupRole(TextEnum): """The role in the group.""" + ADMIN = 'admin' MEMBER = 'member' @@ -26,10 +29,10 @@ class Group(BaseObject): @api_call def get_memberships( - self, - limit: Optional[int] = None, - offset: Optional[int] = None, - fields: Optional[Iterable[str]] = None + self, + limit: Optional[int] = None, + offset: Optional[int] = None, + fields: Optional[Iterable[str]] = None, ) -> Iterable['GroupMembership']: """ Get the membership records for the group, which indicate which users are included in the group. @@ -54,10 +57,10 @@ def get_memberships( @api_call def add_member( - self, - user: 'User', - role: GroupRole = GroupRole.MEMBER, - configurable_permissions: Optional[str] = SDK_VALUE_NOT_SET + self, + user: 'User', + role: GroupRole = GroupRole.MEMBER, + configurable_permissions: Optional[str] = SDK_VALUE_NOT_SET, ) -> 'GroupMembership': """ Add the given user to this group under the given role @@ -86,10 +89,10 @@ def add_member( @api_call def get_collaborations( - self, - limit: Optional[int] = None, - offset: Optional[int] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + offset: Optional[int] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the collaboration for the group using limit-offset paging. diff --git a/boxsdk/object/item.py b/boxsdk/object/item.py index e79aad1c0..ee991aba5 100644 --- a/boxsdk/object/item.py +++ b/boxsdk/object/item.py @@ -19,6 +19,7 @@ class ClassificationType(TextEnum): """An enum of possible classification types""" + PUBLIC = 'Public' INTERNAL = 'Internal' CONFIDENTIAL = 'Confidential' @@ -30,7 +31,9 @@ class Item(BaseItem): _classification_template_key = 'securityClassification-6VMVochwUWo' - def _get_accelerator_upload_url(self, file_id: Optional[str] = None) -> Optional[str]: + def _get_accelerator_upload_url( + self, file_id: Optional[str] = None + ) -> Optional[str]: """ Make an API call to get the Accelerator upload url for either upload a new file or updating an existing file. @@ -53,10 +56,7 @@ def _get_accelerator_upload_url(self, file_id: Optional[str] = None) -> Optional return None def _preflight_check( - self, size: int, - name: str = None, - file_id: str = None, - parent_id: str = None + self, size: int, name: str = None, file_id: str = None, parent_id: str = None ) -> Optional[str]: """ Make an API call to check if certain file can be uploaded to Box or not. @@ -96,7 +96,9 @@ def _preflight_check( return response_json.get('upload_url', None) @api_call - def update_info(self, *, data: dict, etag: Optional[str] = None, **kwargs: Any) -> 'Item': + def update_info( + self, *, data: dict, etag: Optional[str] = None, **kwargs: Any + ) -> 'Item': """ Baseclass override. :param data: @@ -120,7 +122,9 @@ def update_info(self, *, data: dict, etag: Optional[str] = None, **kwargs: Any) return super().update_info(data=data, headers=headers, **kwargs) @api_call - def get(self, *, fields: Iterable[str] = None, etag: Optional[str] = None, **kwargs) -> 'Item': + def get( + self, *, fields: Iterable[str] = None, etag: Optional[str] = None, **kwargs + ) -> 'Item': """ Base class override. @@ -154,7 +158,9 @@ def remove_shared_link(self, *, etag: Optional[str] = None, **kwargs: Any) -> bo return super().remove_shared_link(etag=etag) @api_call - def delete(self, *, params: dict = None, etag: Optional[str] = None, **kwargs) -> bool: + def delete( + self, *, params: dict = None, etag: Optional[str] = None, **kwargs + ) -> bool: """Delete the item. :param params: @@ -222,11 +228,7 @@ def apply_watermark(self) -> 'Watermark': """ self.validate_item_id(self._object_id) url = self.get_url('watermark') - body_attributes = { - 'watermark': { - 'imprint': 'default' - } - } + body_attributes = {'watermark': {'imprint': 'default'}} box_response = self._session.put(url, data=json.dumps(body_attributes)) response = box_response.json() return self.translator.get('watermark')(response['watermark']) @@ -246,12 +248,12 @@ def delete_watermark(self) -> bool: @api_call def collaborate( - self, - accessible_by: Union['User', 'Group'], - role: str, - can_view_path: Optional[bool] = None, - notify: Optional[bool] = None, - fields: Iterable[str] = None + self, + accessible_by: Union['User', 'Group'], + role: str, + can_view_path: Optional[bool] = None, + notify: Optional[bool] = None, + fields: Iterable[str] = None, ) -> 'Collaboration': """Collaborate user or group onto a Box item. @@ -297,12 +299,12 @@ def collaborate( @api_call def collaborate_with_login( - self, - login: str, - role: str, - can_view_path: Optional[bool] = None, - notify: Optional[bool] = None, - fields: Iterable[str] = None + self, + login: str, + role: str, + can_view_path: Optional[bool] = None, + notify: Optional[bool] = None, + fields: Iterable[str] = None, ) -> 'Collaboration': """Collaborate user onto a Box item with the user login. @@ -347,10 +349,10 @@ def collaborate_with_login( @api_call def get_collaborations( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the collaboration. @@ -387,8 +389,7 @@ def add_classification(self, classification: str) -> str: 'Box__Security__Classification__Key': classification, } metadata_classification = self.metadata( - scope='enterprise', - template=self._classification_template_key + scope='enterprise', template=self._classification_template_key ).create(classification_metadata) return metadata_classification['Box__Security__Classification__Key'] @@ -401,7 +402,9 @@ def update_classification(self, classification: str) -> str: :return: The classification updated on the :class:`File` or :class:`Folder. """ - classification_metadata = self.metadata('enterprise', self._classification_template_key) + classification_metadata = self.metadata( + 'enterprise', self._classification_template_key + ) updates = classification_metadata.start_update() updates.add('/Box__Security__Classification__Key', classification) metadata_classification = classification_metadata.update(updates) @@ -421,8 +424,7 @@ def set_classification(self, classification: str) -> str: 'Box__Security__Classification__Key': classification, } return self.metadata( - scope='enterprise', - template=self._classification_template_key + scope='enterprise', template=self._classification_template_key ).set(metadata=classification_metadata)['Box__Security__Classification__Key'] def get_classification(self) -> Optional[str]: @@ -433,7 +435,9 @@ def get_classification(self) -> Optional[str]: The classification on the :class:`File` or :class:`Folder. """ try: - classification = self.metadata('enterprise', self._classification_template_key).get() + classification = self.metadata( + 'enterprise', self._classification_template_key + ).get() except BoxAPIException as err: if err.status == 404 and err.code == "instance_not_found": return None diff --git a/boxsdk/object/legal_hold.py b/boxsdk/object/legal_hold.py index cd1f917d1..03a31961b 100644 --- a/boxsdk/object/legal_hold.py +++ b/boxsdk/object/legal_hold.py @@ -4,8 +4,8 @@ class LegalHold(BaseObject): - """Represents the legal hold policy for a file version""" + _item_type = 'legal_hold' def get_url(self, *args: Any) -> str: diff --git a/boxsdk/object/legal_hold_policy.py b/boxsdk/object/legal_hold_policy.py index 90d863c90..ed4cc42c4 100644 --- a/boxsdk/object/legal_hold_policy.py +++ b/boxsdk/object/legal_hold_policy.py @@ -23,7 +23,9 @@ def get_url(self, *args: Any) -> str: return self._session.get_url('legal_hold_policies', self._object_id, *args) @api_call - def assign(self, assignee: Union['FileVersion', 'File', 'Folder', 'User']) -> 'LegalHoldPolicyAssignment': + def assign( + self, assignee: Union['FileVersion', 'File', 'Folder', 'User'] + ) -> 'LegalHoldPolicyAssignment': """Assign legal hold policy :param assignee: @@ -34,10 +36,7 @@ def assign(self, assignee: Union['FileVersion', 'File', 'Folder', 'User']) -> 'L url = self._session.get_url('legal_hold_policy_assignments') body = { 'policy_id': self.object_id, - 'assign_to': { - 'type': assignee.object_type, - 'id': assignee.object_id - } + 'assign_to': {'type': assignee.object_type, 'id': assignee.object_id}, } response = self._session.post(url, data=json.dumps(body)).json() return self.translator.translate( @@ -47,12 +46,12 @@ def assign(self, assignee: Union['FileVersion', 'File', 'Folder', 'User']) -> 'L @api_call def get_assignments( - self, - assign_to_type: Optional[str] = None, - assign_to_id: Optional[str] = None, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + assign_to_type: Optional[str] = None, + assign_to_id: Optional[str] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the legal hold policy assignment using limit-offset paging. @@ -89,10 +88,10 @@ def get_assignments( @api_call def get_file_version_legal_holds( - self, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get legal holds for a file version. diff --git a/boxsdk/object/legal_hold_policy_assignment.py b/boxsdk/object/legal_hold_policy_assignment.py index db760aafa..e95665e30 100644 --- a/boxsdk/object/legal_hold_policy_assignment.py +++ b/boxsdk/object/legal_hold_policy_assignment.py @@ -2,6 +2,6 @@ class LegalHoldPolicyAssignment(BaseObject): - """Represents the legal hold policy assignment""" + _item_type = 'legal_hold_policy_assignment' diff --git a/boxsdk/object/metadata.py b/boxsdk/object/metadata.py index 674b10a1a..bf2cd98c8 100644 --- a/boxsdk/object/metadata.py +++ b/boxsdk/object/metadata.py @@ -15,6 +15,7 @@ class MetadataUpdate: See https://developer.box.com/en/guides/metadata/instances/update/ for more details. See http://jsonpatch.com/ for details about JSON patch. """ + def __init__(self): self._ops = [] @@ -82,7 +83,9 @@ def test(self, path: str, value: str) -> None: class Metadata(BaseEndpoint): - def __init__(self, session: 'Session', box_object: 'BaseObject', scope: str, template: str): + def __init__( + self, session: 'Session', box_object: 'BaseObject', scope: str, template: str + ): """ :param session: The Box session used to make requests. @@ -100,7 +103,7 @@ def __init__(self, session: 'Session', box_object: 'BaseObject', scope: str, tem self._template = template def get_url(self, *args: Any) -> str: - """ Base class override. """ + """Base class override.""" return self._object.get_url('metadata', self._scope, self._template) @staticmethod @@ -190,5 +193,7 @@ def set(self, metadata: dict) -> 'Metadata': return metadata_value def clone(self, session: 'Session' = None) -> 'Metadata': - """ Base class override. """ - return self.__class__(session or self._session, self._object, self._scope, self._template) + """Base class override.""" + return self.__class__( + session or self._session, self._object, self._scope, self._template + ) diff --git a/boxsdk/object/metadata_cascade_policy.py b/boxsdk/object/metadata_cascade_policy.py index defd5964c..1844a1880 100644 --- a/boxsdk/object/metadata_cascade_policy.py +++ b/boxsdk/object/metadata_cascade_policy.py @@ -39,5 +39,7 @@ def force_apply(self, conflict_resolution: CascadePolicyConflictResolution) -> b body = { 'conflict_resolution': conflict_resolution, } - response = self._session.post(url, data=json.dumps(body), expect_json_response=False) + response = self._session.post( + url, data=json.dumps(body), expect_json_response=False + ) return response.ok diff --git a/boxsdk/object/metadata_template.py b/boxsdk/object/metadata_template.py index 750bbd74e..9bf644360 100644 --- a/boxsdk/object/metadata_template.py +++ b/boxsdk/object/metadata_template.py @@ -26,13 +26,15 @@ def add_enum_option(self, field_key: str, option_key: str) -> None: :param option_key: The option to add """ - self.add_operation({ - 'op': 'addEnumOption', - 'fieldKey': field_key, - 'data': { - 'key': option_key, - }, - }) + self.add_operation( + { + 'op': 'addEnumOption', + 'fieldKey': field_key, + 'data': { + 'key': option_key, + }, + } + ) def add_field(self, field: 'MetadataField') -> None: """ @@ -41,10 +43,12 @@ def add_field(self, field: 'MetadataField') -> None: :param field: The new field to add """ - self.add_operation({ - 'op': 'addField', - 'data': field.json(), - }) + self.add_operation( + { + 'op': 'addField', + 'data': field.json(), + } + ) def edit_template(self, data: dict) -> None: """ @@ -53,10 +57,12 @@ def edit_template(self, data: dict) -> None: :param data: The properties to modify """ - self.add_operation({ - 'op': 'editTemplate', - 'data': data, - }) + self.add_operation( + { + 'op': 'editTemplate', + 'data': data, + } + ) def reorder_enum_options(self, field_key: str, option_keys: List[str]) -> None: """ @@ -67,11 +73,13 @@ def reorder_enum_options(self, field_key: str, option_keys: List[str]) -> None: :param option_keys: The option keys in the desired order """ - self.add_operation({ - 'op': 'reorderEnumOptions', - 'fieldKey': field_key, - 'enumOptionKeys': option_keys, - }) + self.add_operation( + { + 'op': 'reorderEnumOptions', + 'fieldKey': field_key, + 'enumOptionKeys': option_keys, + } + ) def reorder_fields(self, field_keys: List[str]) -> None: """ @@ -80,10 +88,12 @@ def reorder_fields(self, field_keys: List[str]) -> None: :param field_keys: The field keys in the desired order """ - self.add_operation({ - 'op': 'reorderFields', - 'fieldKeys': field_keys, - }) + self.add_operation( + { + 'op': 'reorderFields', + 'fieldKeys': field_keys, + } + ) def edit_field(self, field_key: str, field: 'MetadataField') -> None: """ @@ -94,13 +104,17 @@ def edit_field(self, field_key: str, field: 'MetadataField') -> None: :param field: The updated field values """ - self.add_operation({ - 'op': 'editField', - 'fieldKey': field_key, - 'data': field.json(), - }) + self.add_operation( + { + 'op': 'editField', + 'fieldKey': field_key, + 'data': field.json(), + } + ) - def edit_enum_option_key(self, field_key: str, old_option_key: str, new_option_key: str) -> None: + def edit_enum_option_key( + self, field_key: str, old_option_key: str, new_option_key: str + ) -> None: """ Change the key of an enum field option. @@ -111,14 +125,16 @@ def edit_enum_option_key(self, field_key: str, old_option_key: str, new_option_k :param new_option_key: The new option key """ - self.add_operation({ - 'op': 'editEnumOption', - 'fieldKey': field_key, - 'enumOptionKey': old_option_key, - 'data': { - 'key': new_option_key, - }, - }) + self.add_operation( + { + 'op': 'editEnumOption', + 'fieldKey': field_key, + 'enumOptionKey': old_option_key, + 'data': { + 'key': new_option_key, + }, + } + ) def remove_enum_option(self, field_key: str, option_key: str) -> None: """ @@ -129,11 +145,13 @@ def remove_enum_option(self, field_key: str, option_key: str) -> None: :param option_key: The key of the enum option to remove """ - self.add_operation({ - 'op': 'removeEnumOption', - 'fieldKey': field_key, - 'enumOptionKey': option_key, - }) + self.add_operation( + { + 'op': 'removeEnumOption', + 'fieldKey': field_key, + 'enumOptionKey': option_key, + } + ) def remove_field(self, field_key: str) -> None: """ @@ -142,10 +160,12 @@ def remove_field(self, field_key: str) -> None: :param field_key: The key of the field to remove """ - self.add_operation({ - 'op': 'removeField', - 'fieldKey': field_key, - }) + self.add_operation( + { + 'op': 'removeField', + 'fieldKey': field_key, + } + ) def add_operation(self, operation: dict) -> None: """ @@ -169,13 +189,13 @@ class MetadataField: """Represents a metadata field when creating or updating a metadata template.""" def __init__( - self, - field_type: MetadataFieldType, - display_name: str, - key: Optional[str] = None, - options: Iterable[str] = None, - description: Optional[str] = None, - hidden: Optional[bool] = None + self, + field_type: MetadataFieldType, + display_name: str, + key: Optional[str] = None, + options: Iterable[str] = None, + description: Optional[str] = None, + hidden: Optional[bool] = None, ): """ :param field_type: @@ -234,7 +254,12 @@ class MetadataTemplate(BaseObject): _scope = None _template_key = None - def __init__(self, session: 'Session', object_id: Optional[str], response_object: Optional[dict] = None): + def __init__( + self, + session: 'Session', + object_id: Optional[str], + response_object: Optional[dict] = None, + ): """ :param session: The Box session used to make requests. @@ -250,7 +275,9 @@ def __init__(self, session: 'Session', object_id: Optional[str], response_object self._scope = response_object.get('scope', None) self._template_key = response_object.get('templateKey', None) elif not object_id: - raise ValueError('Metadata template must be constructed with an ID or scope and templateKey') + raise ValueError( + 'Metadata template must be constructed with an ID or scope and templateKey' + ) @property def scope(self) -> Optional[str]: @@ -265,7 +292,9 @@ def get_url(self, *args: Any) -> str: Base class override, since metadata templates have a weird compound ID and non-standard URL format """ if self._scope and self._template_key: - return self._session.get_url('metadata_templates', self._scope, self._template_key, 'schema', *args) + return self._session.get_url( + 'metadata_templates', self._scope, self._template_key, 'schema', *args + ) return super().get_url(*args) @@ -280,7 +309,9 @@ def start_update() -> MetadataTemplateUpdate: return MetadataTemplateUpdate() @api_call - def update_info(self, *, updates: MetadataTemplateUpdate, **kwargs) -> 'MetadataTemplate': + def update_info( + self, *, updates: MetadataTemplateUpdate, **kwargs + ) -> 'MetadataTemplate': # pylint: disable=arguments-differ """ Update a metadata template with a set of update operations. diff --git a/boxsdk/object/retention_policy.py b/boxsdk/object/retention_policy.py index 5541cb603..e82b1f8b1 100644 --- a/boxsdk/object/retention_policy.py +++ b/boxsdk/object/retention_policy.py @@ -14,6 +14,7 @@ class RetentionPolicy(BaseObject): """Represents a Box retention policy.""" + _item_type = 'retention_policy' def get_url(self, *args: Any) -> str: @@ -24,10 +25,10 @@ def get_url(self, *args: Any) -> str: @api_call def assign( - self, - assignee: Union['Folder', 'Enterprise', 'MetadataTemplate'], - fields: Iterable[str] = None, - start_date_field: Optional[str] = None, + self, + assignee: Union['Folder', 'Enterprise', 'MetadataTemplate'], + fields: Iterable[str] = None, + start_date_field: Optional[str] = None, ) -> 'RetentionPolicyAssignment': """Assign a retention policy to a Box item @@ -47,7 +48,7 @@ def assign( 'assign_to': { 'type': assignee.object_type, 'id': assignee.object_id, - } + }, } params = {} if fields is not None: @@ -62,11 +63,11 @@ def assign( @api_call def assignments( - self, - assignment_type: Optional[str] = None, - limit: Optional[int] = None, - marker: Optional[str] = None, - fields: Iterable[str] = None + self, + assignment_type: Optional[str] = None, + limit: Optional[int] = None, + marker: Optional[str] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """Get the assignments for the retention policy. diff --git a/boxsdk/object/retention_policy_assignment.py b/boxsdk/object/retention_policy_assignment.py index ab79d0276..ae36ab72f 100644 --- a/boxsdk/object/retention_policy_assignment.py +++ b/boxsdk/object/retention_policy_assignment.py @@ -7,13 +7,12 @@ class RetentionPolicyAssignment(BaseObject): """Represents a Box retention policy assignment.""" + _item_type = 'retention_policy_assignment' @api_call def get_files_under_retention( - self, - limit: Optional[int] = None, - marker: Optional[str] = None + self, limit: Optional[int] = None, marker: Optional[str] = None ) -> 'MarkerBasedObjectCollection': """ Retrieves all files under retention for a retention policy assignment @@ -26,14 +25,12 @@ def get_files_under_retention( self.session, self.get_url('files_under_retention'), limit=limit, - marker=marker + marker=marker, ) @api_call def get_file_versions_under_retention( - self, - limit: Optional[int] = None, - marker: Optional[str] = None + self, limit: Optional[int] = None, marker: Optional[str] = None ) -> 'MarkerBasedObjectCollection': """ Retrieves all file versions under retention for a retention policy assignment @@ -46,5 +43,5 @@ def get_file_versions_under_retention( self.session, self.get_url('file_versions_under_retention'), limit=limit, - marker=marker + marker=marker, ) diff --git a/boxsdk/object/search.py b/boxsdk/object/search.py index b934d91c5..2e3c44a00 100644 --- a/boxsdk/object/search.py +++ b/boxsdk/object/search.py @@ -1,7 +1,9 @@ import json from typing import Optional, List, Any, Iterable, TYPE_CHECKING, Tuple, Union from .base_endpoint import BaseEndpoint -from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from ..pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from ..pagination.marker_based_object_collection import MarkerBasedObjectCollection from ..util.api_call_decorator import api_call from ..util.deprecation_decorator import deprecated_param @@ -16,12 +18,14 @@ class SearchScope(TextEnum): """Enum of possible serach scopes.""" + USER = 'user_content' ENTERPRISE = 'enterprise_content' class TrashContent(TextEnum): """Enum of trash content values.""" + NONE = 'non_trashed_only' ONLY = 'trashed_only' @@ -32,6 +36,7 @@ class MetadataSearchFilter: but can filter on many fields. See :class:`MetadataSearchFilters`. """ + def __init__(self, template_key: str, scope: str): """ :param template_key: @@ -53,7 +58,7 @@ def as_dict(self) -> dict: return { 'templateKey': self._template_key, 'scope': self._scope, - 'filters': self._field_filters + 'filters': self._field_filters, } def add_value_based_filter(self, field_key: str, value: str) -> None: @@ -68,10 +73,10 @@ def add_value_based_filter(self, field_key: str, value: str) -> None: self._field_filters.update({field_key: value}) def add_range_filter( - self, - field_key: str, - gt_value: Union[str, int, float] = None, - lt_value: Union[str, int, float] = None + self, + field_key: str, + gt_value: Union[str, int, float] = None, + lt_value: Union[str, int, float] = None, ) -> None: """ Add a range filter (used for ranged searches on numbers and dates) @@ -98,6 +103,7 @@ class MetadataSearchFilters: Helper class to encapsulate a list of metadata search filter params (mdfilters API param) See https://developers.box.com/metadata-api/#search for more details """ + def __init__(self): self._filters = [] @@ -143,7 +149,9 @@ def start_metadata_filters() -> MetadataSearchFilters: return MetadataSearchFilters() @staticmethod - def make_single_metadata_filter(template_key: str, scope: str) -> MetadataSearchFilter: + def make_single_metadata_filter( + template_key: str, scope: str + ) -> MetadataSearchFilter: """ Make a single :class:`MetadataSearchFilter` that represents a filter on a template. It must be added to a :class:`MetadataSearchFilters`. @@ -159,25 +167,25 @@ def make_single_metadata_filter(template_key: str, scope: str) -> MetadataSearch @api_call # pylint: disable=too-many-arguments,too-many-locals,too-many-branches def query( - self, - query: str, - limit: int = None, - offset: int = 0, - ancestor_folders: Iterable['Folder'] = None, - file_extensions: Iterable[str] = None, - metadata_filters: MetadataSearchFilters = None, - result_type: str = None, - content_types: Iterable[str] = None, - scope: Optional[str] = None, - created_at_range: Tuple[Optional[str], Optional[str]] = None, - updated_at_range: Tuple[Optional[str], Optional[str]] = None, - size_range: Tuple[Optional[int], Optional[int]] = None, - owner_users: Iterable['User'] = None, - trash_content: Optional[str] = None, - fields: Iterable[str] = None, - sort: Optional[str] = None, - direction: Optional[str] = None, - **kwargs: Any + self, + query: str, + limit: int = None, + offset: int = 0, + ancestor_folders: Iterable['Folder'] = None, + file_extensions: Iterable[str] = None, + metadata_filters: MetadataSearchFilters = None, + result_type: str = None, + content_types: Iterable[str] = None, + scope: Optional[str] = None, + created_at_range: Tuple[Optional[str], Optional[str]] = None, + updated_at_range: Tuple[Optional[str], Optional[str]] = None, + size_range: Tuple[Optional[int], Optional[int]] = None, + owner_users: Iterable['User'] = None, + trash_content: Optional[str] = None, + fields: Iterable[str] = None, + sort: Optional[str] = None, + direction: Optional[str] = None, + **kwargs: Any, ) -> Iterable['Item']: """ Search Box for items matching the given query. @@ -222,7 +230,9 @@ def query( url = self.get_url() additional_params = {'query': query} if ancestor_folders is not None: - additional_params['ancestor_folder_ids'] = ','.join([folder.object_id for folder in ancestor_folders]) + additional_params['ancestor_folder_ids'] = ','.join( + [folder.object_id for folder in ancestor_folders] + ) if file_extensions is not None: additional_params['file_extensions'] = ','.join(file_extensions) if metadata_filters is not None: @@ -234,13 +244,21 @@ def query( if scope is not None: additional_params['scope'] = scope if created_at_range is not None: - additional_params['created_at_range'] = f'{created_at_range[0] or ""},{created_at_range[1] or ""}' + additional_params['created_at_range'] = ( + f'{created_at_range[0] or ""},{created_at_range[1] or ""}' + ) if updated_at_range is not None: - additional_params['updated_at_range'] = f'{updated_at_range[0] or ""},{updated_at_range[1] or ""}' + additional_params['updated_at_range'] = ( + f'{updated_at_range[0] or ""},{updated_at_range[1] or ""}' + ) if size_range is not None: - additional_params['size_range'] = f'{size_range[0] or ""},{size_range[1] or ""}' + additional_params['size_range'] = ( + f'{size_range[0] or ""},{size_range[1] or ""}' + ) if owner_users is not None: - additional_params['owner_user_ids'] = ','.join([user.object_id for user in owner_users]) + additional_params['owner_user_ids'] = ','.join( + [user.object_id for user in owner_users] + ) if trash_content is not None: additional_params['trash_content'] = trash_content if sort is not None: @@ -260,19 +278,23 @@ def query( return_full_pages=False, ) - @deprecated_param(name="use_index", position=5, message="Parameter will be ignored. See docs for details.") + @deprecated_param( + name="use_index", + position=5, + message="Parameter will be ignored. See docs for details.", + ) @api_call def metadata_query( - self, - from_template: str, - ancestor_folder_id: str, - query: Optional[str] = None, - query_params: Optional[dict] = None, - use_index: Optional[str] = None, - order_by: List[dict] = None, - marker: Optional[str] = None, - limit: int = None, - fields: Iterable[Optional[str]] = None + self, + from_template: str, + ancestor_folder_id: str, + query: Optional[str] = None, + query_params: Optional[dict] = None, + use_index: Optional[str] = None, + order_by: List[dict] = None, + marker: Optional[str] = None, + limit: int = None, + fields: Iterable[Optional[str]] = None, ) -> 'BoxObjectCollection': # pylint:disable=unused-argument """Query Box items by their metadata. @@ -298,10 +320,7 @@ def metadata_query( An iterator of the item search results """ url = super().get_url('metadata_queries/execute_read') - data = { - 'from': from_template, - 'ancestor_folder_id': ancestor_folder_id - } + data = {'from': from_template, 'ancestor_folder_id': ancestor_folder_id} if query is not None: data['query'] = query if query_params is not None: @@ -317,31 +336,31 @@ def metadata_query( fields=fields, additional_params=data, return_full_pages=False, - use_post=True + use_post=True, ) @api_call # pylint: disable=too-many-arguments,too-many-locals,too-many-branches def query_with_shared_links( - self, - query: str, - limit: int = None, - offset: int = 0, - ancestor_folders: Iterable['Folder'] = None, - file_extensions: Iterable[str] = None, - metadata_filters: MetadataSearchFilters = None, - result_type: str = None, - content_types: Iterable[str] = None, - scope: Optional[str] = None, - created_at_range: Tuple[Optional[str], Optional[str]] = None, - updated_at_range: Tuple[Optional[str], Optional[str]] = None, - size_range: Tuple[Optional[int], Optional[int]] = None, - owner_users: Iterable['User'] = None, - trash_content: Optional[str] = None, - fields: Iterable[str] = None, - sort: Optional[str] = None, - direction: Optional[str] = None, - **kwargs: Any + self, + query: str, + limit: int = None, + offset: int = 0, + ancestor_folders: Iterable['Folder'] = None, + file_extensions: Iterable[str] = None, + metadata_filters: MetadataSearchFilters = None, + result_type: str = None, + content_types: Iterable[str] = None, + scope: Optional[str] = None, + created_at_range: Tuple[Optional[str], Optional[str]] = None, + updated_at_range: Tuple[Optional[str], Optional[str]] = None, + size_range: Tuple[Optional[int], Optional[int]] = None, + owner_users: Iterable['User'] = None, + trash_content: Optional[str] = None, + fields: Iterable[str] = None, + sort: Optional[str] = None, + direction: Optional[str] = None, + **kwargs: Any, ) -> Iterable['Item']: """ Search Box for items matching the given query. May also include items that are only accessible via recently used shared links. @@ -386,7 +405,9 @@ def query_with_shared_links( url = self.get_url() additional_params = {'query': query, 'include_recent_shared_links': True} if ancestor_folders is not None: - additional_params['ancestor_folder_ids'] = ','.join([folder.object_id for folder in ancestor_folders]) + additional_params['ancestor_folder_ids'] = ','.join( + [folder.object_id for folder in ancestor_folders] + ) if file_extensions is not None: additional_params['file_extensions'] = ','.join(file_extensions) if metadata_filters is not None: @@ -398,13 +419,21 @@ def query_with_shared_links( if scope is not None: additional_params['scope'] = scope if created_at_range is not None: - additional_params['created_at_range'] = f'{created_at_range[0] or ""},{created_at_range[1] or ""}' + additional_params['created_at_range'] = ( + f'{created_at_range[0] or ""},{created_at_range[1] or ""}' + ) if updated_at_range is not None: - additional_params['updated_at_range'] = f'{updated_at_range[0] or ""},{updated_at_range[1] or ""}' + additional_params['updated_at_range'] = ( + f'{updated_at_range[0] or ""},{updated_at_range[1] or ""}' + ) if size_range is not None: - additional_params['size_range'] = f'{size_range[0] or ""},{size_range[1] or ""}' + additional_params['size_range'] = ( + f'{size_range[0] or ""},{size_range[1] or ""}' + ) if owner_users is not None: - additional_params['owner_user_ids'] = ','.join([user.object_id for user in owner_users]) + additional_params['owner_user_ids'] = ','.join( + [user.object_id for user in owner_users] + ) if trash_content is not None: additional_params['trash_content'] = trash_content if sort is not None: diff --git a/boxsdk/object/sign_request.py b/boxsdk/object/sign_request.py index c5e7e4493..75292b46d 100644 --- a/boxsdk/object/sign_request.py +++ b/boxsdk/object/sign_request.py @@ -10,6 +10,7 @@ class SignRequest(BaseObject): Sign Requests are used to request e-signatures on documents from signers. A Sign Request can refer to one or more Box Files and can be sent to one or more Box Sign Request Signers. """ + _item_type = 'sign-request' def get_url(self, *args: Any) -> str: @@ -44,5 +45,7 @@ def resend(self) -> bool: Whether the operation succeeded. """ url = self.get_url('resend') - response = self._session.post(url, skip_retry_codes={202}, expect_json_response=False) + response = self._session.post( + url, skip_retry_codes={202}, expect_json_response=False + ) return response.ok diff --git a/boxsdk/object/sign_template.py b/boxsdk/object/sign_template.py index a5b8d0094..5ade5ea0a 100644 --- a/boxsdk/object/sign_template.py +++ b/boxsdk/object/sign_template.py @@ -7,6 +7,7 @@ class SignTemplate(BaseObject): """ Represents a Sign Template used by Box Sign """ + _item_type = 'sign-template' def get_url(self, *args: Any) -> str: diff --git a/boxsdk/object/storage_policy.py b/boxsdk/object/storage_policy.py index bf4b976a4..9e2ca6ac2 100644 --- a/boxsdk/object/storage_policy.py +++ b/boxsdk/object/storage_policy.py @@ -61,7 +61,7 @@ def create_assignment(self, user: 'User') -> 'StoragePolicyAssignment': 'assigned_to': { 'type': user.object_type, 'id': user.object_id, - } + }, } response = self._session.post(url, data=json.dumps(body)).json() return self.translator.translate( diff --git a/boxsdk/object/task.py b/boxsdk/object/task.py index 9af632534..a0f693a83 100644 --- a/boxsdk/object/task.py +++ b/boxsdk/object/task.py @@ -13,6 +13,7 @@ class Task(BaseObject): """Represents a Box task.""" + _item_type = 'task' @api_call diff --git a/boxsdk/object/task_assignment.py b/boxsdk/object/task_assignment.py index 50b30dc1f..3f59f0dac 100644 --- a/boxsdk/object/task_assignment.py +++ b/boxsdk/object/task_assignment.py @@ -4,6 +4,7 @@ class ResolutionState(TextEnum): """An enum of possible resolution states""" + COMPLETED = 'completed' INCOMPLETE = 'incomplete' APPROVED = 'approved' diff --git a/boxsdk/object/terms_of_service.py b/boxsdk/object/terms_of_service.py index 50ac32a52..499638269 100644 --- a/boxsdk/object/terms_of_service.py +++ b/boxsdk/object/terms_of_service.py @@ -12,12 +12,14 @@ class TermsOfServiceType(TextEnum): """An enum of possible terms of service types""" + MANAGED = 'managed' EXTERNAL = 'external' class TermsOfServiceStatus(TextEnum): """An enum of possible terms of service status""" + ENABLED = 'enabled' DISABLED = 'disabled' @@ -27,7 +29,9 @@ class TermsOfService(BaseObject): _item_type = 'terms_of_service' - def get_user_status(self, user: Optional['User'] = None) -> Optional['TermsOfServiceUserStatus']: + def get_user_status( + self, user: Optional['User'] = None + ) -> Optional['TermsOfServiceUserStatus']: """ Get the terms of service user status. @@ -77,7 +81,9 @@ def reject(self, user: Optional['User'] = None) -> 'TermsOfServiceUserStatus': """ return self.set_user_status(is_accepted=False, user=user) - def set_user_status(self, is_accepted: bool, user: Optional['User'] = None) -> 'TermsOfServiceUserStatus': + def set_user_status( + self, is_accepted: bool, user: Optional['User'] = None + ) -> 'TermsOfServiceUserStatus': """ Create a terms of service user status. @@ -112,5 +118,7 @@ def set_user_status(self, is_accepted: bool, user: Optional['User'] = None) -> ' except BoxAPIException as err: if err.status == 409: user_status = self.get_user_status(user) - translated_response = user_status.update_info(data={'is_accepted': is_accepted}) + translated_response = user_status.update_info( + data={'is_accepted': is_accepted} + ) return translated_response diff --git a/boxsdk/object/terms_of_service_user_status.py b/boxsdk/object/terms_of_service_user_status.py index 81b46561f..140faedf0 100644 --- a/boxsdk/object/terms_of_service_user_status.py +++ b/boxsdk/object/terms_of_service_user_status.py @@ -9,22 +9,20 @@ class TermsOfServiceUserStatus(BaseObject): _item_type = 'terms_of_service_user_status' def get_url(self, *args: Any) -> str: - return self._session.get_url('terms_of_service_user_statuses', self._object_id, *args) + return self._session.get_url( + 'terms_of_service_user_statuses', self._object_id, *args + ) def accept(self) -> 'TermsOfServiceUserStatus': """ Accept a term of service. """ - body = { - 'is_accepted': True - } + body = {'is_accepted': True} return self.update_info(data=body) def reject(self) -> 'TermsOfServiceUserStatus': """ Reject a term of service. """ - body = { - 'is_accepted': False - } + body = {'is_accepted': False} return self.update_info(data=body) diff --git a/boxsdk/object/trash.py b/boxsdk/object/trash.py index d15c26ee4..2cb959b0e 100644 --- a/boxsdk/object/trash.py +++ b/boxsdk/object/trash.py @@ -5,7 +5,9 @@ from .base_endpoint import BaseEndpoint -from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from ..pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from ..util.api_call_decorator import api_call if TYPE_CHECKING: @@ -42,11 +44,11 @@ def get_item(self, item: 'BaseItem', fields: Iterable[str] = None) -> 'BaseItem' @api_call def restore_item( - self, - item: 'BaseItem', - name: Optional[str] = None, - parent_folder: Optional['Folder'] = None, - fields: Iterable[str] = None + self, + item: 'BaseItem', + name: Optional[str] = None, + parent_folder: Optional['Folder'] = None, + fields: Iterable[str] = None, ) -> 'BaseItem': """ Restores an item from the trash. Could be files, folders, or weblinks. @@ -71,8 +73,7 @@ def restore_item( params = {} if fields: params['fields'] = ','.join(fields) - box_response = self._session.post( - url, data=json.dumps(body), params=params) + box_response = self._session.post(url, data=json.dumps(body), params=params) response = box_response.json() return self.translator.translate( session=self._session, @@ -102,7 +103,7 @@ def get_items( marker: Optional[str] = None, use_marker: bool = False, sort: Optional[str] = None, - direction: Optional[str] = None + direction: Optional[str] = None, ) -> 'BoxObjectCollection': """ Using limit-offset paging, get the files, folders and web links that are in the user's trash. diff --git a/boxsdk/object/upload_session.py b/boxsdk/object/upload_session.py index 8ccbe5476..fac5aef0a 100644 --- a/boxsdk/object/upload_session.py +++ b/boxsdk/object/upload_session.py @@ -11,7 +11,9 @@ from boxsdk.session.session import Session from boxsdk.config import API from .base_object import BaseObject -from ..pagination.limit_offset_based_dict_collection import LimitOffsetBasedDictCollection +from ..pagination.limit_offset_based_dict_collection import ( + LimitOffsetBasedDictCollection, +) if TYPE_CHECKING: from boxsdk.pagination.box_object_collection import BoxObjectCollection @@ -24,7 +26,11 @@ class UploadSession(BaseObject): _default_upload_url = API.UPLOAD_URL def __init__( - self, session: Session, object_id: str, response_object: dict = None, use_upload_session_urls: bool = True + self, + session: Session, + object_id: str, + response_object: dict = None, + use_upload_session_urls: bool = True, ): super().__init__(session, object_id, response_object) self._use_upload_session_urls = use_upload_session_urls @@ -34,17 +40,23 @@ def get_url(self, *args: Any, url_key: str = None) -> str: Base class override. Endpoint is a little different - it's /files/upload_sessions. """ session_endpoints = getattr(self, 'session_endpoints', {}) - if self._use_upload_session_urls and url_key in session_endpoints and self.session.api_config.UPLOAD_URL == self._default_upload_url: + if ( + self._use_upload_session_urls + and url_key in session_endpoints + and self.session.api_config.UPLOAD_URL == self._default_upload_url + ): return session_endpoints[url_key] return self._session.get_url( - f'{self._parent_item_type}s/{self._item_type}s', - self._object_id, - *args - ).replace(self.session.api_config.BASE_API_URL, self.session.api_config.UPLOAD_URL) + f'{self._parent_item_type}s/{self._item_type}s', self._object_id, *args + ).replace( + self.session.api_config.BASE_API_URL, self.session.api_config.UPLOAD_URL + ) @api_call - def get_parts(self, limit: Optional[int] = None, offset: Optional[int] = None) -> 'BoxObjectCollection': + def get_parts( + self, limit: Optional[int] = None, offset: Optional[int] = None + ) -> 'BoxObjectCollection': """ Get a list of parts uploaded so far. @@ -66,11 +78,11 @@ def get_parts(self, limit: Optional[int] = None, offset: Optional[int] = None) - @api_call def upload_part_bytes( - self, - part_bytes: bytes, - offset: int, - total_size: int, - part_content_sha1: Optional[bytes] = None + self, + part_bytes: bytes, + offset: int, + total_size: int, + part_content_sha1: Optional[bytes] = None, ) -> dict: """ Upload a part of a file. @@ -93,7 +105,9 @@ def upload_part_bytes( sha1.update(part_bytes) part_content_sha1 = sha1.digest() - range_end = min(offset + self.part_size - 1, total_size - 1) # pylint:disable=no-member + range_end = min( + offset + self.part_size - 1, total_size - 1 + ) # pylint:disable=no-member headers = { 'Content-Type': 'application/octet-stream', 'Digest': f'SHA={base64.b64encode(part_content_sha1).decode("utf-8")}', @@ -108,11 +122,11 @@ def upload_part_bytes( @api_call def commit( - self, - content_sha1: bytes, - parts: Iterable[Optional[dict]] = None, - file_attributes: dict = None, - etag: Optional[str] = None + self, + content_sha1: bytes, + parts: Iterable[Optional[dict]] = None, + file_attributes: dict = None, + etag: Optional[str] = None, ) -> Optional['File']: """ Commit a multiput upload. @@ -169,12 +183,13 @@ def abort(self) -> bool: """ box_response = self._session.delete( - self.get_url(url_key='abort'), - expect_json_response=False + self.get_url(url_key='abort'), expect_json_response=False ) return box_response.ok - def get_chunked_uploader_for_stream(self, content_stream: IO[bytes], file_size: int) -> ChunkedUploader: + def get_chunked_uploader_for_stream( + self, content_stream: IO[bytes], file_size: int + ) -> ChunkedUploader: """ Instantiate the chunked upload instance and create upload session. diff --git a/boxsdk/object/user.py b/boxsdk/object/user.py index 9c8c7ba2d..ea1ac6632 100644 --- a/boxsdk/object/user.py +++ b/boxsdk/object/user.py @@ -3,7 +3,9 @@ from typing import TYPE_CHECKING, Optional, Iterable, IO from .base_object import BaseObject -from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from ..pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from ..pagination.marker_based_object_collection import MarkerBasedObjectCollection from ..util.api_call_decorator import api_call @@ -40,7 +42,9 @@ def add_email_alias(self, email: str) -> 'EmailAlias': ) @api_call - def get_email_aliases(self, limit: Optional[int] = None, fields: Iterable[str] = None) -> 'BoxObjectCollection': + def get_email_aliases( + self, limit: Optional[int] = None, fields: Iterable[str] = None + ) -> 'BoxObjectCollection': """ Gets an list of email aliases for a user. @@ -76,10 +80,10 @@ def remove_email_alias(self, email_alias: 'EmailAlias') -> bool: @api_call def transfer_content( - self, - destination_user: 'User', - notify: Optional[bool] = None, - fields: Iterable[str] = None + self, + destination_user: 'User', + notify: Optional[bool] = None, + fields: Iterable[str] = None, ) -> 'Folder': """ Move all of the items owned by a user into a new folder in another user's account. @@ -131,10 +135,10 @@ def get_storage_policy_assignment(self) -> 'StoragePolicyAssignment': @api_call def get_group_memberships( - self, - limit: Optional[int] = None, - offset: Optional[int] = None, - fields: Iterable[str] = None + self, + limit: Optional[int] = None, + offset: Optional[int] = None, + fields: Iterable[str] = None, ) -> 'BoxObjectCollection': """ Get the entries in the user group membership using limit-offset paging. @@ -183,10 +187,14 @@ def upload_avatar(self, image_path: str) -> str: """ with open(image_path, 'rb') as image_stream: image_extension = os.path.splitext(image_path)[-1][1:] - return self.upload_avatar_stream(image_stream=image_stream, image_extension=image_extension) + return self.upload_avatar_stream( + image_stream=image_stream, image_extension=image_extension + ) @api_call - def upload_avatar_stream(self, image_stream: IO[bytes], image_extension: str) -> str: + def upload_avatar_stream( + self, image_stream: IO[bytes], image_extension: str + ) -> str: """ Upload avatar image to user account. Supported formats are JPG, JPEG and PNG. Maximum allowed file size is 1MB and resolution 1024x1024 pixels. @@ -196,7 +204,13 @@ def upload_avatar_stream(self, image_stream: IO[bytes], image_extension: str) -> :return: URLs to existing user avatars that were updated """ url = self.get_url('avatar') - files = {'pic': (f'avatar.{image_extension}', image_stream, f'image/{image_extension}')} + files = { + 'pic': ( + f'avatar.{image_extension}', + image_stream, + f'image/{image_extension}', + ) + } response = self._session.post(url, files=files) return response.json()['pic_urls'] diff --git a/boxsdk/object/web_link.py b/boxsdk/object/web_link.py index 999de75f5..79890dc4d 100644 --- a/boxsdk/object/web_link.py +++ b/boxsdk/object/web_link.py @@ -13,13 +13,13 @@ class WebLink(BaseItem): @api_call def create_shared_link( - self, - *, - access: Optional[str] = None, - unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, - password: Optional[str] = None, - vanity_name: Optional[str] = None, - **kwargs: Any + self, + *, + access: Optional[str] = None, + unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + **kwargs: Any ) -> 'WebLink': """ Baseclass override. @@ -50,18 +50,18 @@ def create_shared_link( access=access, unshared_at=unshared_at, password=password, - vanity_name=vanity_name + vanity_name=vanity_name, ) @api_call def get_shared_link( - self, - *, - access: Optional[str] = None, - unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, - password: Optional[str] = None, - vanity_name: Optional[str] = None, - **kwargs: Any + self, + *, + access: Optional[str] = None, + unshared_at: Union[datetime, str, None] = SDK_VALUE_NOT_SET, + password: Optional[str] = None, + vanity_name: Optional[str] = None, + **kwargs: Any ) -> str: """ Baseclass override. @@ -90,7 +90,7 @@ def get_shared_link( access=access, unshared_at=unshared_at, password=password, - vanity_name=vanity_name + vanity_name=vanity_name, ) @api_call diff --git a/boxsdk/object/webhook.py b/boxsdk/object/webhook.py index 5df32ffec..8064a7017 100644 --- a/boxsdk/object/webhook.py +++ b/boxsdk/object/webhook.py @@ -13,10 +13,10 @@ class Webhook(BaseObject): @staticmethod def validate_message( - body: bytes, - headers: dict, - primary_signature_key: str, - secondary_signature_key: str = None + body: bytes, + headers: dict, + primary_signature_key: str, + secondary_signature_key: str = None, ) -> bool: """ Validates a `Webhook` message. @@ -34,12 +34,18 @@ def validate_message( """ primary_signature = _compute_signature(body, headers, primary_signature_key) - if primary_signature is not None and hmac.compare_digest(primary_signature, headers.get('box-signature-primary')): + if primary_signature is not None and hmac.compare_digest( + primary_signature, headers.get('box-signature-primary') + ): return True if secondary_signature_key: - secondary_signature = _compute_signature(body, headers, secondary_signature_key) - if secondary_signature is not None and hmac.compare_digest(secondary_signature, headers.get('box-signature-secondary')): + secondary_signature = _compute_signature( + body, headers, secondary_signature_key + ) + if secondary_signature is not None and hmac.compare_digest( + secondary_signature, headers.get('box-signature-secondary') + ): return True return False diff --git a/boxsdk/pagination/box_object_collection.py b/boxsdk/pagination/box_object_collection.py index e7da45512..2ce8dff0a 100644 --- a/boxsdk/pagination/box_object_collection.py +++ b/boxsdk/pagination/box_object_collection.py @@ -25,17 +25,18 @@ class BoxObjectCollection(Iterator, ABC): will be used to retrieve the next page of Box objects. This pointer can be used when requesting new BoxObjectCollection instances that start off from a particular page, instead of from the very beginning. """ + _page_constructor = Page def __init__( - self, - session: 'Session', - url: str, - limit: Optional[int] = None, - fields: Optional[Iter[str]] = None, - additional_params: Optional[dict] = None, - return_full_pages: bool = False, - use_post: bool = False + self, + session: 'Session', + url: str, + limit: Optional[int] = None, + fields: Optional[Iter[str]] = None, + additional_params: Optional[dict] = None, + return_full_pages: bool = False, + use_post: bool = False, ): """ :param session: @@ -117,7 +118,11 @@ def _load_next_page(self) -> dict: if self._use_post: if self._fields: params['fields'] = self._fields - box_response = self._session.post(self._url, data=json.dumps(params), headers={b'Content-Type': b'application/json'}) + box_response = self._session.post( + self._url, + data=json.dumps(params), + headers={b'Content-Type': b'application/json'}, + ) else: if self._fields: params['fields'] = ','.join(self._fields) diff --git a/boxsdk/pagination/limit_offset_based_dict_collection.py b/boxsdk/pagination/limit_offset_based_dict_collection.py index 5732f110f..b07d2a94d 100644 --- a/boxsdk/pagination/limit_offset_based_dict_collection.py +++ b/boxsdk/pagination/limit_offset_based_dict_collection.py @@ -4,4 +4,5 @@ class LimitOffsetBasedDictCollection(LimitOffsetBasedObjectCollection): """Represents a limit/offset-based collection of simple dicts, which are not translated into objects.""" + _page_constructor = DictPage diff --git a/boxsdk/pagination/limit_offset_based_object_collection.py b/boxsdk/pagination/limit_offset_based_object_collection.py index 73c94bc1b..f9f4a1732 100644 --- a/boxsdk/pagination/limit_offset_based_object_collection.py +++ b/boxsdk/pagination/limit_offset_based_object_collection.py @@ -15,14 +15,14 @@ class LimitOffsetBasedObjectCollection(BoxObjectCollection): """ def __init__( - self, - session: 'Session', - url: str, - limit: Optional[int] = None, - fields: Iterator[str] = None, - additional_params: Optional[dict] = None, - return_full_pages: bool = False, - offset: int = 0, + self, + session: 'Session', + url: str, + limit: Optional[int] = None, + fields: Iterator[str] = None, + additional_params: Optional[dict] = None, + return_full_pages: bool = False, + offset: int = 0, ): """ :param session: @@ -75,7 +75,9 @@ def _update_pointer_to_next_page(self, response_object: dict) -> None: # a runtime error. if self._limit <= 0 < old_limit: self._offset = total_count # Disable additional paging. - raise RuntimeError(f'API returned limit={self._limit}, cannot continue paging') + raise RuntimeError( + f'API returned limit={self._limit}, cannot continue paging' + ) # de-none-ify the _offset value so that the arthimatic below works self._offset = self._offset or 0 diff --git a/boxsdk/pagination/marker_based_dict_collection.py b/boxsdk/pagination/marker_based_dict_collection.py index c68b9b8c5..0970cda70 100644 --- a/boxsdk/pagination/marker_based_dict_collection.py +++ b/boxsdk/pagination/marker_based_dict_collection.py @@ -4,4 +4,5 @@ class MarkerBasedDictCollection(MarkerBasedObjectCollection): """Represents a marker-based collection of simple dicts, which are not translated into objects.""" + _page_constructor = DictPage diff --git a/boxsdk/pagination/marker_based_object_collection.py b/boxsdk/pagination/marker_based_object_collection.py index 082c831a6..860e973b0 100644 --- a/boxsdk/pagination/marker_based_object_collection.py +++ b/boxsdk/pagination/marker_based_object_collection.py @@ -15,16 +15,16 @@ class MarkerBasedObjectCollection(BoxObjectCollection): """ def __init__( - self, - session: 'Session', - url: str, - limit: Optional[int] = None, - fields: Iterator[str] = None, - additional_params: Optional[dict] = None, - return_full_pages: bool = False, - marker: Optional[str] = None, - supports_limit_offset_paging: bool = False, - use_post: bool = False + self, + session: 'Session', + url: str, + limit: Optional[int] = None, + fields: Iterator[str] = None, + additional_params: Optional[dict] = None, + return_full_pages: bool = False, + marker: Optional[str] = None, + supports_limit_offset_paging: bool = False, + use_post: bool = False, ): """ :param session: @@ -59,7 +59,7 @@ def __init__( fields=fields, additional_params=additional_params, return_full_pages=return_full_pages, - use_post=use_post + use_post=use_post, ) self._marker = marker self._supports_limit_offset_paging = supports_limit_offset_paging diff --git a/boxsdk/pagination/page.py b/boxsdk/pagination/page.py index ef032db6c..0408cbcd1 100644 --- a/boxsdk/pagination/page.py +++ b/boxsdk/pagination/page.py @@ -15,6 +15,7 @@ class Page(Sequence): The Page makes available detailed response data for page requests. """ + _item_entries_key_name = "entries" def __init__(self, session: 'Session', response_object: dict): diff --git a/boxsdk/session/box_request.py b/boxsdk/session/box_request.py index b255c5c2b..30e58c95d 100644 --- a/boxsdk/session/box_request.py +++ b/boxsdk/session/box_request.py @@ -15,6 +15,7 @@ class BoxRequest: :param auto_session_renewal: Whether or not the session can be automatically renewed if the request fails. :param expect_json_response: Whether or not the API response must be JSON. """ + url: str = attr.ib() method: Optional[str] = attr.ib(default='GET') headers: Optional[dict] = attr.ib(default=attr.Factory(dict)) diff --git a/boxsdk/session/session.py b/boxsdk/session/session.py index e60f5f0a6..f16a22980 100644 --- a/boxsdk/session/session.py +++ b/boxsdk/session/session.py @@ -34,15 +34,16 @@ class Session: """ Box API session. Provides automatic retry of failed requests. """ + def __init__( - self, - network_layer: 'Network' = None, - default_headers: Optional['dict'] = None, - translator: Translator = None, - default_network_request_kwargs: Optional['dict'] = None, - api_config: API = None, - client_config: Client = None, - proxy_config: Optional[Proxy] = None, + self, + network_layer: 'Network' = None, + default_headers: Optional['dict'] = None, + translator: Translator = None, + default_network_request_kwargs: Optional['dict'] = None, + api_config: API = None, + client_config: Client = None, + proxy_config: Optional[Proxy] = None, ): """ :param network_layer: @@ -203,7 +204,9 @@ def as_user(self, user: 'User') -> 'Session': kwargs['default_headers']['As-User'] = user.object_id return self.__class__(**kwargs) - def with_shared_link(self, shared_link: str, shared_link_password: str = None) -> 'Session': + def with_shared_link( + self, shared_link: str, shared_link_password: str = None + ) -> 'Session': """ Returns a new session object with default headers set up to make requests using the shared link for auth. @@ -213,10 +216,14 @@ def with_shared_link(self, shared_link: str, shared_link_password: str = None) - The password for the shared link. """ kwargs = self.get_constructor_kwargs() - kwargs['default_headers'].update(get_shared_link_header(shared_link, shared_link_password)) + kwargs['default_headers'].update( + get_shared_link_header(shared_link, shared_link_password) + ) return self.__class__(**kwargs) - def with_default_network_request_kwargs(self, extra_network_parameters: dict) -> 'Session': + def with_default_network_request_kwargs( + self, extra_network_parameters: dict + ) -> 'Session': kwargs = self.get_constructor_kwargs() kwargs['default_network_request_kwargs'].update(extra_network_parameters) return self.__class__(**kwargs) @@ -224,7 +231,9 @@ def with_default_network_request_kwargs(self, extra_network_parameters: dict) -> # We updated our retry strategy to use exponential backoff instead of the header returned from the API response. # This is something we can remove in latter major bumps. # pylint: disable=unused-argument - def get_retry_after_time(self, attempt_number: int, retry_after_header: Optional[str]) -> Number: + def get_retry_after_time( + self, attempt_number: int, retry_after_header: Optional[str] + ) -> Number: """ Get the amount of time to wait before retrying the API request, using the attempt number that failed to calculate the retry time for the next retry attempt. @@ -245,12 +254,18 @@ def get_retry_after_time(self, attempt_number: int, retry_after_header: Optional pass min_randomization = 1 - self._retry_randomization_factor max_randomization = 1 + self._retry_randomization_factor - randomization = (random.uniform(0, 1) * (max_randomization - min_randomization)) + min_randomization + randomization = ( + random.uniform(0, 1) * (max_randomization - min_randomization) + ) + min_randomization exponential = math.pow(2, attempt_number) return exponential * self._retry_base_interval * randomization @staticmethod - def _raise_on_unsuccessful_request(network_response: 'NetworkResponse', request: '_BoxRequest', raised_exception: Exception) -> None: + def _raise_on_unsuccessful_request( + network_response: 'NetworkResponse', + request: '_BoxRequest', + raised_exception: Exception, + ) -> None: """ Raise an exception if the request was unsuccessful. @@ -271,13 +286,15 @@ def _raise_on_unsuccessful_request(network_response: 'NetworkResponse', request: raise BoxAPIException( status=network_response.status_code, headers=network_response.headers, - code=response_json.get('code', None) or response_json.get('error', None), - message=response_json.get('message', None) or response_json.get('error_description', None), + code=response_json.get('code', None) + or response_json.get('error', None), + message=response_json.get('message', None) + or response_json.get('error_description', None), request_id=response_json.get('request_id', None), url=request.url, method=request.method, context_info=response_json.get('context_info', None), - network_response=network_response + network_response=network_response, ) if not Session._is_json_response_if_expected(network_response, request): @@ -291,7 +308,9 @@ def _raise_on_unsuccessful_request(network_response: 'NetworkResponse', request: ) @staticmethod - def _is_json_response_if_expected(network_response: 'NetworkResponse', request: '_BoxRequest') -> bool: + def _is_json_response_if_expected( + network_response: 'NetworkResponse', request: '_BoxRequest' + ) -> bool: """ Validate that the response is json if the request expects json response. @@ -303,13 +322,13 @@ def _is_json_response_if_expected(network_response: 'NetworkResponse', request: return not request.expect_json_response or is_json_response(network_response) def _prepare_and_send_request( - self, - method: str, - url: str, - headers: dict = None, - auto_session_renewal: bool = True, - expect_json_response: bool = True, - **kwargs: Any + self, + method: str, + url: str, + headers: dict = None, + auto_session_renewal: bool = True, + expect_json_response: bool = True, + **kwargs: Any, ) -> 'NetworkResponse': """ Prepare a request to be sent to the Box API. @@ -328,7 +347,9 @@ def _prepare_and_send_request( files = kwargs.get('files') kwargs['file_stream_positions'] = None if files: - kwargs['file_stream_positions'] = {name: file_tuple[1].tell() for name, file_tuple in files.items()} + kwargs['file_stream_positions'] = { + name: file_tuple[1].tell() for name, file_tuple in files.items() + } attempt_number = 0 request_headers = self._get_request_headers() request_headers.update(headers or {}) @@ -352,16 +373,27 @@ def _prepare_and_send_request( network_response = None if 'EOF occurred in violation of protocol' in str(request_exc): reauthentication_needed = True - elif any(text in str(request_exc) for text in [ - 'Connection aborted', 'Connection broken', 'Connection reset' - ]): + elif any( + text in str(request_exc) + for text in [ + 'Connection aborted', + 'Connection broken', + 'Connection reset', + ] + ): reauthentication_needed = False else: raise while True: retry = self._get_retry_request_callable( - network_response, attempt_number, request, skip_retry_codes, reauthentication_needed, **kwargs) + network_response, + attempt_number, + request, + skip_retry_codes, + reauthentication_needed, + **kwargs, + ) if retry is None or attempt_number >= API.MAX_RETRY_ATTEMPTS: if network_response is None: @@ -377,13 +409,13 @@ def _prepare_and_send_request( return network_response def _get_retry_request_callable( - self, - network_response: Optional['NetworkResponse'], - attempt_number: int, - request: '_BoxRequest', - skip_retry_codes: Set[int], - session_renewal_needed: bool = False, - **kwargs: Any + self, + network_response: Optional['NetworkResponse'], + attempt_number: int, + request: '_BoxRequest', + skip_retry_codes: Set[int], + session_renewal_needed: bool = False, + **kwargs: Any, ) -> Optional[Callable]: """ Get a callable that retries a request for certain types of failure. @@ -405,7 +437,11 @@ def _get_retry_request_callable( """ # pylint:disable=unused-argument # pylint:disable=line-too-long - if network_response is None or (network_response.ok and request.method == 'GET' and not self._is_json_response_if_expected(network_response, request)): + if network_response is None or ( + network_response.ok + and request.method == 'GET' + and not self._is_json_response_if_expected(network_response, request) + ): return partial( self._network_layer.retry_after, self.get_retry_after_time(attempt_number, None), @@ -413,10 +449,16 @@ def _get_retry_request_callable( ) code = network_response.status_code - if (code in (202, 429) or code >= 500) and code not in skip_retry_codes and not self._is_server_auth_type(kwargs): + if ( + (code in (202, 429) or code >= 500) + and code not in skip_retry_codes + and not self._is_server_auth_type(kwargs) + ): return partial( self._network_layer.retry_after, - self.get_retry_after_time(attempt_number, network_response.headers.get('Retry-After', None)), + self.get_retry_after_time( + attempt_number, network_response.headers.get('Retry-After', None) + ), self._send_request, ) return None @@ -444,16 +486,22 @@ def _prepare_proxy(self) -> Optional[dict]: proxy = {} if self._proxy_config.URL is None: return None - if self._proxy_config.AUTH and {'user', 'password'} <= set(self._proxy_config.AUTH): + if self._proxy_config.AUTH and {'user', 'password'} <= set( + self._proxy_config.AUTH + ): host = self._proxy_config.URL address = host.split('//')[1] - proxy_string = f'http://{self._proxy_config.AUTH.get("user", None)}:' \ - f'{self._proxy_config.AUTH.get("password", None)}@{address}' + proxy_string = ( + f'http://{self._proxy_config.AUTH.get("user", None)}:' + f'{self._proxy_config.AUTH.get("password", None)}@{address}' + ) elif self._proxy_config.AUTH is None: proxy_string = self._proxy_config.URL else: - raise BoxException("The proxy auth dict you provided does not match pattern " - "{'user': 'example_user', 'password': 'example_password'}") + raise BoxException( + "The proxy auth dict you provided does not match pattern " + "{'user': 'example_user', 'password': 'example_password'}" + ) proxy['http'] = proxy_string proxy['https'] = proxy['http'] @@ -469,7 +517,10 @@ def _send_request(self, request: '_BoxRequest', **kwargs: Any) -> 'NetworkRespon # Reset stream positions to what they were when the request was made so the same data is sent even if this # is a retried attempt. files, file_stream_positions, stream_file_content = ( - kwargs.get('files'), kwargs.pop('file_stream_positions'), kwargs.pop('stream_file_content', True)) + kwargs.get('files'), + kwargs.pop('file_stream_positions'), + kwargs.pop('stream_file_content', True), + ) request_kwargs = self._default_network_request_kwargs.copy() request_kwargs.update(kwargs) proxy_dict = self._prepare_proxy() @@ -493,7 +544,7 @@ def _send_request(self, request: '_BoxRequest', **kwargs: Any) -> 'NetworkRespon access_token=request.access_token, headers=request.headers, log_response_content=request.expect_json_response, - **request_kwargs + **request_kwargs, ) return network_response @@ -530,13 +581,13 @@ def _renew_session(self, access_token_used: Optional[str]) -> str: return new_access_token def _get_retry_request_callable( - self, - network_response: Optional['NetworkResponse'], - attempt_number: int, - request: '_BoxRequest', - skip_retry_codes: Set[int], - session_renewal_needed: bool = False, - **kwargs: Any + self, + network_response: Optional['NetworkResponse'], + attempt_number: int, + request: '_BoxRequest', + skip_retry_codes: Set[int], + session_renewal_needed: bool = False, + **kwargs: Any, ) -> Callable: """ Get a callable that retries a request for certain types of failure. @@ -565,7 +616,7 @@ def _get_retry_request_callable( request, skip_retry_codes, session_renewal_needed, - **kwargs + **kwargs, ) def _send_request(self, request: '_BoxRequest', **kwargs: Any) -> 'NetworkResponse': diff --git a/boxsdk/util/api_call_decorator.py b/boxsdk/util/api_call_decorator.py index db963b4fc..51355d02e 100644 --- a/boxsdk/util/api_call_decorator.py +++ b/boxsdk/util/api_call_decorator.py @@ -31,8 +31,12 @@ def __init__(self, func_that_makes_an_api_call: Callable): self.__name__ = func_that_makes_an_api_call.__name__ update_wrapper(self, func_that_makes_an_api_call) - def __call__(self, cloneable_instance: 'Cloneable', *args: Any, **kwargs: Any) -> Any: - return self.__get__(cloneable_instance, type(cloneable_instance))(*args, **kwargs) + def __call__( + self, cloneable_instance: 'Cloneable', *args: Any, **kwargs: Any + ) -> Any: + return self.__get__(cloneable_instance, type(cloneable_instance))( + *args, **kwargs + ) def __get__(self, _instance: Any, owner: Any) -> Any: # `APICallWrapper` is imitating a function. For native functions, @@ -57,7 +61,11 @@ def call(instance, *args, **kwargs): if extra_network_parameters: # If extra_network_parameters is specified, then clone the instance, and specify the parameters # as the defaults to be used. - instance = instance.clone(instance.session.with_default_network_request_kwargs(extra_network_parameters)) + instance = instance.clone( + instance.session.with_default_network_request_kwargs( + extra_network_parameters + ) + ) method = self._func_that_makes_an_api_call.__get__(instance, owner) return method(*args, **kwargs) diff --git a/boxsdk/util/chunked_uploader.py b/boxsdk/util/chunked_uploader.py index 85c4d860c..ab7064916 100644 --- a/boxsdk/util/chunked_uploader.py +++ b/boxsdk/util/chunked_uploader.py @@ -13,7 +13,9 @@ class ChunkedUploader: - def __init__(self, upload_session: 'UploadSession', content_stream: IO[bytes], file_size: int): + def __init__( + self, upload_session: 'UploadSession', content_stream: IO[bytes], file_size: int + ): """ The initializer for the :class:`ChunkedUploader` @@ -47,9 +49,14 @@ def start(self) -> Optional['File']: An uploaded :class:`File` or None if session was not processed """ if self._is_aborted: - raise BoxException('The upload has been previously aborted. Please retry upload with a new upload session.') - - futures = [self._executor.submit(self._upload_part) for _ in range(self._upload_session.total_parts)] + raise BoxException( + 'The upload has been previously aborted. Please retry upload with a new upload session.' + ) + + futures = [ + self._executor.submit(self._upload_part) + for _ in range(self._upload_session.total_parts) + ] self._upload(futures) return self._commit_and_erase_stream_reference_when_succeed() @@ -63,7 +70,9 @@ def resume(self) -> Optional['File']: An uploaded :class:`File` or None if session was not processed """ if self._is_aborted: - raise BoxException('The upload has been previously aborted. Please retry upload with a new upload session.') + raise BoxException( + 'The upload has been previously aborted. Please retry upload with a new upload session.' + ) self._executor = ThreadPoolExecutor(max_workers=API.CHUNK_UPLOAD_THREADS) parts = self._upload_session.get_parts() @@ -71,8 +80,15 @@ def resume(self) -> Optional['File']: self._part_definitions[part['offset']] = part with self._lock: - futures = [self._executor.submit(lambda item=part: self._upload_part(item)) for part in self._inflight_parts.values()] - for _ in range(self._upload_session.total_parts - self._chunk_index - len(self._inflight_parts)): + futures = [ + self._executor.submit(lambda item=part: self._upload_part(item)) + for part in self._inflight_parts.values() + ] + for _ in range( + self._upload_session.total_parts + - self._chunk_index + - len(self._inflight_parts) + ): futures.append(self._executor.submit(self._upload_part)) self._upload(futures) @@ -102,7 +118,9 @@ def _upload(self, futures: List[Future]) -> None: self._executor.shutdown(wait=True) raise exc - self._part_array = sorted(self._part_definitions.values(), key=lambda part: part['offset']) + self._part_array = sorted( + self._part_definitions.values(), key=lambda part: part['offset'] + ) def _upload_part(self, task=None): if isinstance(task, InflightPart): @@ -135,7 +153,9 @@ def _get_next_part(self) -> 'InflightPart': offset = self._chunk_index * self._upload_session.part_size self._chunk_index += 1 while copied_length < self._upload_session.part_size: - bytes_read = self._content_stream.read(self._upload_session.part_size - copied_length) + bytes_read = self._content_stream.read( + self._upload_session.part_size - copied_length + ) if bytes_read is None: # stream returns none when no bytes are ready currently but there are # potentially more bytes in the stream to be read. @@ -149,7 +169,9 @@ def _get_next_part(self) -> 'InflightPart': def _commit_and_erase_stream_reference_when_succeed(self): content_sha1 = self._sha1.digest() - commit_result = self._upload_session.commit(content_sha1=content_sha1, parts=self._part_array) + commit_result = self._upload_session.commit( + content_sha1=content_sha1, parts=self._part_array + ) # Remove file stream reference when uploading file succeeded if commit_result is not None: self._content_stream = None @@ -159,7 +181,13 @@ def _commit_and_erase_stream_reference_when_succeed(self): class InflightPart: - def __init__(self, offset: int, chunk: bytes, upload_session: 'UploadSession', total_size: int): + def __init__( + self, + offset: int, + chunk: bytes, + upload_session: 'UploadSession', + total_size: int, + ): """ The initializer for the :class:`InflightPart` object. @@ -199,7 +227,5 @@ def upload(self) -> dict: The uploaded part record. """ return self._upload_session.upload_part_bytes( - part_bytes=self.chunk, - offset=self.offset, - total_size=self._total_size + part_bytes=self.chunk, offset=self.offset, total_size=self._total_size ) diff --git a/boxsdk/util/deprecation_decorator.py b/boxsdk/util/deprecation_decorator.py index 89e893caa..587d2ac10 100644 --- a/boxsdk/util/deprecation_decorator.py +++ b/boxsdk/util/deprecation_decorator.py @@ -6,11 +6,15 @@ def deprecated(message: str): def deprecated_decorator(func: Callable) -> Any: def deprecated_func(*args, **kwargs): warnings.simplefilter('default', DeprecationWarning) - warnings.warn(f'{func.__name__} is a deprecated function. {message}', - category=DeprecationWarning, - stacklevel=2) + warnings.warn( + f'{func.__name__} is a deprecated function. {message}', + category=DeprecationWarning, + stacklevel=2, + ) return func(*args, **kwargs) + return deprecated_func + return deprecated_decorator @@ -22,8 +26,10 @@ def deprecated_func(*args, **kwargs): warnings.warn( f'{func.__name__} function parameter `{name}` at position {position} is deprecated. {message}', category=DeprecationWarning, - stacklevel=2 + stacklevel=2, ) return func(*args, **kwargs) + return deprecated_func + return deprecated_decorator diff --git a/boxsdk/util/enum.py b/boxsdk/util/enum.py index 2296c5ae5..d11758a91 100644 --- a/boxsdk/util/enum.py +++ b/boxsdk/util/enum.py @@ -125,7 +125,9 @@ def __getattr__(cls, name): raise attribute_error def __iter__(cls): - return chain(super().__iter__(), chain.from_iterable(map(iter, cls.__subclasses__()))) + return chain( + super().__iter__(), chain.from_iterable(map(iter, cls.__subclasses__())) + ) def __len__(cls): return super().__len__() + sum(map(len, cls.__subclasses__())) diff --git a/boxsdk/util/log.py b/boxsdk/util/log.py index 49278adcb..a8d87fc44 100644 --- a/boxsdk/util/log.py +++ b/boxsdk/util/log.py @@ -54,7 +54,7 @@ def sanitize_proxy_value(value: str) -> str: return re.sub( '^(.*://)(.*):(.*)(@.*)$', lambda repl: f'{repl.group(1)}{"---"}:{"---"}{repl.group(4)}', - value + value, ) def sanitize_dictionary(self, dictionary: Mapping) -> Mapping: @@ -77,9 +77,9 @@ def sanitize_dictionary(self, dictionary: Mapping) -> Mapping: def setup_logging( - stream_or_file: Optional[Union[str, IO]] = _no_logger, - debug: Optional[bool] = False, - name: Optional[str] = None + stream_or_file: Optional[Union[str, IO]] = _no_logger, + debug: Optional[bool] = False, + name: Optional[str] = None, ) -> None: """ Create a logger for communicating with the user or writing to log files. diff --git a/boxsdk/util/multipart_stream.py b/boxsdk/util/multipart_stream.py index b7b7a5be0..10c53bf24 100644 --- a/boxsdk/util/multipart_stream.py +++ b/boxsdk/util/multipart_stream.py @@ -9,6 +9,7 @@ class MultipartStream(MultipartEncoder): is encoded before files. This allows a server to process information in the data before receiving the file bytes. """ + def __init__(self, data, files): fields = OrderedDict() for k in data: diff --git a/boxsdk/util/shared_link.py b/boxsdk/util/shared_link.py index 03b4e2f7e..e8a97f298 100644 --- a/boxsdk/util/shared_link.py +++ b/boxsdk/util/shared_link.py @@ -9,6 +9,8 @@ def get_shared_link_header(shared_link: str, password: str = None) -> dict: :return: The item referred to by the shared link. """ - shared_link_password = f'&shared_link_password={password}' if password is not None else '' + shared_link_password = ( + f'&shared_link_password={password}' if password is not None else '' + ) box_api_header = f'shared_link={shared_link}{shared_link_password}' return {'BoxApi': box_api_header} diff --git a/boxsdk/util/translator.py b/boxsdk/util/translator.py index 8e39cd858..00e61e4d5 100644 --- a/boxsdk/util/translator.py +++ b/boxsdk/util/translator.py @@ -60,9 +60,13 @@ class Translator(ChainMap): # `BoxSession`. # - Calling `client.translator.register()` on an existing `Client`. # :type _default_translator: :class:`Translator` - _default_translator = {} # Will be set to a `Translator` instance below, after the class is defined. + _default_translator = ( + {} + ) # Will be set to a `Translator` instance below, after the class is defined. - def __init__(self, *translation_maps: Dict[str, 'BaseAPIJSONObjectMeta'], **kwargs: Any): + def __init__( + self, *translation_maps: Dict[str, 'BaseAPIJSONObjectMeta'], **kwargs: Any + ): """Baseclass override. :param translation_maps: @@ -105,7 +109,9 @@ def register(self, type_name: str, box_cls: 'BaseAPIJSONObjectMeta') -> Any: """ self[type_name] = box_cls - def get(self, key: str, default: 'BaseAPIJSONObjectMeta' = None) -> 'BaseAPIJSONObjectMeta': + def get( + self, key: str, default: 'BaseAPIJSONObjectMeta' = None + ) -> 'BaseAPIJSONObjectMeta': """Get the box object class associated with the given type name. :param key: @@ -116,6 +122,7 @@ def get(self, key: str, default: 'BaseAPIJSONObjectMeta' = None) -> 'BaseAPIJSON """ # pylint:disable=import-outside-toplevel from boxsdk.object.base_object import BaseObject + if default is None: default = BaseObject return super().get(key, default) @@ -139,7 +146,9 @@ def translate(self, session: 'Session', response_object: dict) -> Any: object_type = response_object.get('type', None) object_class = self.get(object_type) if object_type is not None else None # Parent classes have the ability to skip fields that they do not want translated - fields_to_skip = object_class.untranslated_fields() if object_class is not None else () + fields_to_skip = ( + object_class.untranslated_fields() if object_class is not None else () + ) for key in response_object: if key in fields_to_skip: translated_obj[key] = response_object[key] @@ -147,7 +156,9 @@ def translate(self, session: 'Session', response_object: dict) -> Any: if isinstance(response_object[key], dict): translated_obj[key] = self.translate(session, response_object[key]) elif isinstance(response_object[key], list): - translated_obj[key] = [self.translate(session, o) for o in response_object[key]] + translated_obj[key] = [ + self.translate(session, o) for o in response_object[key] + ] else: translated_obj[key] = response_object[key] @@ -169,4 +180,6 @@ def translate(self, session: 'Session', response_object: dict) -> Any: return translated_obj -Translator._default_translator = Translator(extend_default_translator=False) # pylint:disable=protected-access +Translator._default_translator = Translator( + extend_default_translator=False +) # pylint:disable=protected-access diff --git a/demo/auth.py b/demo/auth.py index 4e33bae7a..45e0cd9f9 100644 --- a/demo/auth.py +++ b/demo/auth.py @@ -6,7 +6,6 @@ from boxsdk import OAuth2 - CLIENT_ID = '' # Insert Box client ID here CLIENT_SECRET = '' # Insert Box client secret here @@ -20,7 +19,9 @@ def __init__(self, *args, **kwargs): def run(self, app): server_cls = self.options.get('server_class', WSGIServer) handler_cls = self.options.get('handler_class', WSGIRequestHandler) - self._server = make_server(self.host, self.port, app, server_cls, handler_cls) + self._server = make_server( + self.host, self.port, app, server_cls, handler_cls + ) self._server.serve_forever() def stop(self): diff --git a/demo/example.py b/demo/example.py index 12d4ff0ce..56b5d045d 100644 --- a/demo/example.py +++ b/demo/example.py @@ -26,10 +26,14 @@ def run_collab_examples(client): collab_folder = root_folder.create_subfolder('collab folder') try: print(f'Folder {collab_folder.get()["name"]} created') - collaboration = collab_folder.add_collaborator('someone@example.com', CollaborationRole.VIEWER) + collaboration = collab_folder.add_collaborator( + 'someone@example.com', CollaborationRole.VIEWER + ) print('Created a collaboration') try: - modified_collaboration = collaboration.update_info(role=CollaborationRole.EDITOR) + modified_collaboration = collaboration.update_info( + role=CollaborationRole.EDITOR + ) print(f'Modified a collaboration: {modified_collaboration.role}') finally: collaboration.delete() @@ -76,10 +80,14 @@ def upload_file(client): def upload_accelerator(client): root_folder = client.folder(folder_id='0') file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'file.txt') - a_file = root_folder.upload(file_path, file_name='i-am-a-file.txt', upload_using_accelerator=True) + a_file = root_folder.upload( + file_path, file_name='i-am-a-file.txt', upload_using_accelerator=True + ) try: print(f'{a_file.get()["name"]} uploaded via Accelerator: ') - file_v2_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'file_v2.txt') + file_v2_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'file_v2.txt' + ) a_file = a_file.update_contents(file_v2_path, upload_using_accelerator=True) print(f'{a_file.get()["name"]} updated via Accelerator: ') finally: @@ -104,7 +112,9 @@ def update_file(client): file_v1 = root_folder.upload(file_path, file_name='file_v1.txt') try: # print f'File content after upload: {file_v1.content()}' - file_v2_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'file_v2.txt') + file_v2_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'file_v2.txt' + ) file_v2 = file_v1.update_contents(file_v2_path) # print f'File content after update: {file_v2.content()}' finally: @@ -215,7 +225,9 @@ def run_groups_example(client): except BoxAPIException as ex: if ex.status != 403: raise - print('The authenticated user does not have permissions to manage groups. Skipping the test of this demo.') + print( + 'The authenticated user does not have permissions to manage groups. Skipping the test of this demo.' + ) return print('New group:', new_group.name, new_group.id) @@ -233,11 +245,16 @@ def run_groups_example(client): print('The id of that membership: ', group_membership.object_id) group_membership.delete() - print('After deleting that membership, the group has a membership of: ', len(list(new_group.membership()))) + print( + 'After deleting that membership, the group has a membership of: ', + len(list(new_group.membership())), + ) new_group.delete() groups_after_deleting_demo = client.groups() - has_been_deleted = not any(g.name == 'renamed_box_sdk_demo_group' for g in groups_after_deleting_demo) + has_been_deleted = not any( + g.name == 'renamed_box_sdk_demo_group' for g in groups_after_deleting_demo + ) print('The new group has been deleted: ', has_been_deleted) diff --git a/demo/music_player.py b/demo/music_player.py index d30ba9fee..03e3e2079 100644 --- a/demo/music_player.py +++ b/demo/music_player.py @@ -21,7 +21,9 @@ def _authenticate(self): def _get_all_mp3_files(self, client): # music_folder = client.folder(folder_id='0').get_subfolder('music') - return client.search().query(query='*.mp3', limit=100, offset=0, file_extensions=['mp3']) + return client.search().query( + query='*.mp3', limit=100, offset=0, file_extensions=['mp3'] + ) def play(self): for item in self._mp3_files: diff --git a/docs/source/conf.py b/docs/source/conf.py index c2db4faae..bfb90256f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,12 +16,12 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -49,6 +49,7 @@ # built documents. # import pkg_resources + try: release = pkg_resources.get_distribution('boxsdk').version except pkg_resources.DistributionNotFound: @@ -70,9 +71,9 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -80,27 +81,27 @@ # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- @@ -112,26 +113,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -141,62 +142,62 @@ # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'boxsdkdoc' @@ -204,59 +205,52 @@ # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', + # Latex figure (float) alignment + #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'boxsdk.tex', 'boxsdk Documentation', - 'Author', 'manual'), + ('index', 'boxsdk.tex', 'boxsdk Documentation', 'Author', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'boxsdk', 'boxsdk Documentation', - ['Author'], 1) -] +man_pages = [('index', 'boxsdk', 'boxsdk Documentation', ['Author'], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -265,22 +259,28 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'boxsdk', 'boxsdk Documentation', - 'Author', 'boxsdk', 'One line description of project.', - 'Miscellaneous'), + ( + 'index', + 'boxsdk', + 'boxsdk Documentation', + 'Author', + 'boxsdk', + 'One line description of project.', + 'Miscellaneous', + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- @@ -292,62 +292,62 @@ epub_copyright = '2014, Author' # The basename for the epub file. It defaults to the project name. -#epub_basename = u'boxsdk' +# epub_basename = u'boxsdk' # The HTML theme for the epub output. Since the default themes are not optimized # for small screen space, using the same theme for HTML and epub output is # usually not wise. This defaults to 'epub', a theme designed to save visual # space. -#epub_theme = 'epub' +# epub_theme = 'epub' # The language of the text. It defaults to the language option # or 'en' if the language is not set. -#epub_language = '' +# epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' +# epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. -#epub_identifier = '' +# epub_identifier = '' # A unique identification for the text. -#epub_uid = '' +# epub_uid = '' # A tuple containing the cover image and cover page html template filenames. -#epub_cover = () +# epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. -#epub_guide = () +# epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. -#epub_pre_files = [] +# epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. -#epub_post_files = [] +# epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 +# epub_tocdepth = 3 # Allow duplicate toc entries. -#epub_tocdup = True +# epub_tocdup = True # Choose between 'default' and 'includehidden'. -#epub_tocscope = 'default' +# epub_tocscope = 'default' # Fix unsupported image types using the Pillow. -#epub_fix_images = False +# epub_fix_images = False # Scale large images. -#epub_max_image_width = 0 +# epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. -#epub_show_urls = 'inline' +# epub_show_urls = 'inline' # If false, no index is generated. -#epub_use_index = True +# epub_use_index = True diff --git a/docs/usage/README.md b/docs/usage/README.md index f7fd6f5d7..7102329b7 100644 --- a/docs/usage/README.md +++ b/docs/usage/README.md @@ -5,7 +5,7 @@ object basing on the value of the `type` field. If you want to get the raw respo you can access response dict using `response_object` property. Example below will print the raw json response returned by the Get file information endpoint. -``` python +```python file = client.file('1122334455').get() print(json.dumps(file.response_object)) ``` diff --git a/docs/usage/ai.md b/docs/usage/ai.md index 0a9d4da51..784a91925 100644 --- a/docs/usage/ai.md +++ b/docs/usage/ai.md @@ -1,5 +1,4 @@ -AI -== +# AI AI allows to send an intelligence request to supported large language models and returns an answer based on the provided prompt and items. @@ -12,14 +11,12 @@ AI allows to send an intelligence request to supported large language models and -Send AI request ------------------------- +## Send AI request Calling the [`client.send_ai_question(items, prompt, mode, ai_agent)`][send-ai-question] method will send an AI request to the supported large language models. The `items` parameter is a list of items to be processed by the LLM, often files. The `prompt` provided by the client to be answered by the LLM. The prompt's length is limited to 10000 characters. The `mode` specifies if this request is for a single or multiple items. If you select `single_item_qa` the items array can have one element only. Selecting `multiple_item_qa` allows you to provide up to 25 items. The `ai_agent` specifies the AI agent which will be used to handle queries. - - + ```python items = [{ "id": "1582915952443", @@ -33,7 +30,7 @@ ai_agent = { } } answer = client.send_ai_question( - items=items, + items=items, prompt="What is this file?", mode="single_item_qa", ai_agent=ai_agent @@ -46,12 +43,12 @@ It usually takes a few seconds for the file to be indexed and available for the [send-ai-question]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.send_ai_question -Send AI text generation request ------------------------- +## Send AI text generation request Calling the [`client.send_ai_text_gen(dialogue_history, items, prompt, ai_agent)`][send-ai-text-gen] method will send an AI text generation request to the supported large language models. The `dialogue_history` parameter is history of prompts and answers previously passed to the LLM. This provides additional context to the LLM in generating the response. The `items` parameter is a list of items to be processed by the LLM, often files. The `prompt` provided by the client to be answered by the LLM. The prompt's length is limited to 10000 characters. The `ai_agent` specifies the AI agent which will be used for generating text. + ```python items = [{ "id": "1582915952443", @@ -85,12 +82,12 @@ print(answer) [send-ai-text-gen]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.send_ai_text_gen -Get AI agent default configuration ------------------------- +## Get AI agent default configuration To get an AI agent default configuration call the [`client.get_ai_agent_default_config(mode, language, model)`][get-ai-agent-default] method. The `mode` parameter filters the agent configuration to be returned. It can be either `ask` or `text_gen`. The `language` parameter specifies the ISO language code to return the agent config for. If the language is not supported, the default agent configuration is returned. The `model` parameter specifies the model for which the default agent configuration should be returned. + ```python config = client.get_ai_agent_default_config( mode='text_gen', diff --git a/docs/usage/authentication.md b/docs/usage/authentication.md index bce264e8c..e122c934f 100644 --- a/docs/usage/authentication.md +++ b/docs/usage/authentication.md @@ -1,5 +1,4 @@ -Authentication -============== +# Authentication The Box API uses OAuth2 for authentication, which can be difficult to implement. The SDK makes it easier by providing classes that handle obtaining tokens and @@ -27,8 +26,7 @@ overview of how the Box API handles authentication. -Ways to Authenticate --------------------- +## Ways to Authenticate ### Developer Token @@ -76,6 +74,7 @@ To create a [`Client`][client_class] non-interactively with a developer token, c object with the `access_token` set to the developer token and construct the client with that. + ```python from boxsdk import Client, OAuth2 @@ -97,14 +96,15 @@ print(f'My user ID is {me.id}') ### Server Auth with JWT Authenticating with a JWT requires some extra dependencies. To get them, simply + ``` pip install "boxsdk[jwt]" ``` Server auth allows your application to authenticate itself with the Box API -for a given enterprise. By default, your application has a +for a given enterprise. By default, your application has a [Service Account](https://developer.box.com/en/guides/authentication/user-types/app-users/) -that represents it and can perform API calls. The Service Account is separate +that represents it and can perform API calls. The Service Account is separate from the Box accounts of the application developer and the enterprise admin of any enterprise that has authorized the app — files stored in that account are not accessible in any other account by default, and vice versa. @@ -115,6 +115,7 @@ to configure your SDK instance and create a client to make calls as the Service Account by calling the appropriate static [`JWTAuth`][jwt_auth_class] method: + ```python from boxsdk import JWTAuth, Client @@ -128,6 +129,7 @@ Otherwise, you'll need to provide the necessary configuration fields directly to the [`JWTAuth`][jwt_auth_class] constructor: + ```python from boxsdk import JWTAuth, Client @@ -149,16 +151,17 @@ service_account_client = Client(auth) App auth applications also often have associated App Users, which are [created and managed directly by the application](https://developer.box.com/en/guides/authentication/user-types/app-users/) — they do not have normal login credentials, and can only be accessed through -the Box API by the application that created them. You may authenticate as the +the Box API by the application that created them. You may authenticate as the Service Account to provision and manage users, or as an individual app user to -make calls as that user. See the [API documentation](https://developer.box.com/) +make calls as that user. See the [API documentation](https://developer.box.com/) for detailed instructions on how to use app auth. Clients for making calls as an App User can be created with the same [`JWTAuth`][jwt_auth_class] -constructor as in the above examples, similarly to creating a Service Account client. Simply pass the +constructor as in the above examples, similarly to creating a Service Account client. Simply pass the [`User`][user_class] object for the app user instead of an `enterprise_id` when constructing the auth instance: + ```python app_user = service_account_client.user(user_id='APP_USER_ID') @@ -178,7 +181,6 @@ app_user_client = Client(app_user_auth) [jwt_auth_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.auth.html#boxsdk.auth.jwt_auth.JWTAuth [user_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User - ### Client Credentials Grant Allows you to obtain an access token by having client credentials and secret with enterprise or user ID, @@ -201,13 +203,14 @@ Obtained token is valid for specified amount of time, it will be refreshed autom #### Obtaining Service Account token -The [Service Account](https://developer.box.com/guides/getting-started/user-types/service-account//) +The [Service Account](https://developer.box.com/guides/getting-started/user-types/service-account//) is separate from the Box accounts of the application developer and the enterprise admin of any enterprise that has authorized the app — files stored in that account are not accessible in any other account by default, and vice versa. To obtain service account you will have to provide enterprise ID with client id and secret: + ```python auth = CCGAuth( client_id="YOUR_CLIENT_ID", @@ -230,16 +233,16 @@ auth = CCGAuth( user="YOUR_USER_ID" ) ``` + In order to enable obtaining user token you have to go to your application configuration that can be found [here](https://app.box.com/developers/console). In`Configuration` tab, in section `Advanced Features` select `Generate user access tokens`. Do not forget to re-authorize application if it was already authorized. - ### Traditional 3-Legged OAuth2 If your application needs to integrate with existing Box users who will provide their login credentials to grant your application access to their account, you -will need to go through the standard OAuth2 login flow. A detailed guide for +will need to go through the standard OAuth2 login flow. A detailed guide for this process is available in the [Authentication with OAuth API documentation](https://developer.box.com/en/guides/authentication/oauth2/). @@ -260,6 +263,7 @@ The first step in the process is to redirect the user to the Box Authorize URL, your application's redirect URL. + ```python from boxsdk import OAuth2 @@ -282,13 +286,14 @@ The SDK will keep the tokens in memory for the duration of the Python script run #### Authenticate (Get Token Pair) If you navigate the user to the auth_url, the user will be redirected to -`https://YOUR_REDIRECT_URL?code=YOUR_AUTH_CODE&state=CSRF_TOKEN` after they log in to Box. After getting the auth code, +`https://YOUR_REDIRECT_URL?code=YOUR_AUTH_CODE&state=CSRF_TOKEN` after they log in to Box. After getting the auth code, you will be able to exchange it for an access token and refresh token. The SDK handles all the work for you; all you need to do is call [`oauth.authenticate(auth_code)`][authenticate] with the auth code pulled from the query parameters of the incoming URL: + ```python from boxsdk import Client @@ -309,6 +314,7 @@ You can also instantiate a client given the access and refresh token. You first oauth object you then pass it into your [Client][client_class] object to instantiate your client. Finally, you can begin making calls with your client. + ```python from boxsdk import Client, OAuth2 @@ -336,19 +342,19 @@ To use the primary or secondary access token generated in the Developer Console, simply create a [`Client`][client_class] with that token: + ```python from boxsdk import Client, OAuth2 auth = OAuth2( - client_id='YOUR_CLIENT_ID', - client_secret='', + client_id='YOUR_CLIENT_ID', + client_secret='', access_token='APP_ACCESS_TOKEN_GOES_HERE' ) client = Client(auth) ``` -As-User -------- +## As-User The As-User header is used by enterprise admins to make API calls on behalf of their enterprise's users. This requires the API request to pass an @@ -360,10 +366,11 @@ access token belonging to an admin-level user or Service Account with appropriat privileges to make As-User calls. Calling the [`client.as_user(user)`][as_user] method with the [`User`][user_class] creates a new client to impersonate -the provided user. All calls made with the new client will be made in context of the impersonated user, leaving the +the provided user. All calls made with the new client will be made in context of the impersonated user, leaving the original client unmodified. + ```python user_to_impersonate = client.user(user_id='USER_ID_GOES_HERE') user_client = client.as_user(user_to_impersonate) @@ -371,12 +378,11 @@ user_client = client.as_user(user_to_impersonate) [as_user]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.cloneable.Cloneable.as_user -Downscoping token --------------- +## Downscoping token You can downscope a client's access token for one with a lower scope, in order to restrict the permissions for a child client or to pass to a less secure -location (e.g. a browser-based app). This is useful if you want to use the +location (e.g. a browser-based app). This is useful if you want to use the [Box UI Elements](https://developer.box.com/en/guides/embed/ui-elements/), since they generally do not need full read/write permissions to run. @@ -387,6 +393,7 @@ scope, restricted to a single file, suitable for the This method returns a [`TokenResponse`][token_response] object with the downscoped token information. + ```python target_file = client.file(file_id='FILE_ID_HERE') token_info = client.downscope_token(['item_preview'], target_file) @@ -398,18 +405,19 @@ downscoped_client = Client( ) ) ``` + But bear in mind that there is no way of refreshing this token, and you will need to add you own logic to do that. [downscope_token]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.downscope_token [token_response]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.auth.html#boxsdk.auth.oauth2.TokenResponse -Revoking Tokens ---------------- +## Revoking Tokens To revoke the tokens contained in an [`OAuth2`][oauth2_class] instance, removing the ability to call the Box API, call [`oauth.revoke()`][revoke]. + ```python oauth.revoke() ``` diff --git a/docs/usage/classifications.md b/docs/usage/classifications.md index d0c77023f..524c38fd2 100644 --- a/docs/usage/classifications.md +++ b/docs/usage/classifications.md @@ -1,7 +1,6 @@ -Classifications -=============== +# Classifications -Classifications are a type of metadata that allows users and applications +Classifications are a type of metadata that allows users and applications to define and assign a content classification to files and folders. Classifications use the metadata APIs to add and remove classifications, and @@ -11,7 +10,6 @@ assign them to files. For more details on metadata templates please see the - - [Add initial classifications](#add-initial-classifications) - [List all classifications](#list-all-classifications) - [Add another classification](#add-another-classification) @@ -29,8 +27,7 @@ assign them to files. For more details on metadata templates please see the -Add initial classifications ---------------------------- +## Add initial classifications If an enterprise does not already have a classification defined, the first classification(s) can be added with the @@ -38,7 +35,8 @@ can be added with the method. -```python + +```python from boxsdk.object.metadata_template import MetadataField, MetadataFieldType fields = [ @@ -48,28 +46,28 @@ fields = [ template = client.create_metadata_template('Classification', fields, template_key='securityClassification-6VMVochwUWo') ``` -List all classifications ------------------------- +## List all classifications To retrieve a list of all the classifications in an enterprise call the [`client.metadata_template(scope, template_key)`](https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.metadata_template) -method to get the classifications template, which will contain a list of all the +method to get the classifications template, which will contain a list of all the classifications. + ```python template = client.metadata_template('enterprise', 'securityClassification-6VMVochwUWo').get() ``` -Add another classification --------------------------- +## Add another classification To add another classification, call the -[`template.start_update()`][start_update] API to start making changes to the +[`template.start_update()`][start_update] API to start making changes to the template, and then call the [`template.update_info(updates=new_updates)`][update_info] with the changes to apply to the template. + ```python template = client.metadata_template('enterprise', 'securityClassification-6VMVochwUWo') new_updates = template.start_update() @@ -80,15 +78,15 @@ updated_template = template.update_info(updates=new_updates) [start_update]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_template.MetadataTemplate.start_update [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_template.MetadataTemplate.update_info -Update a classification ------------------------ +## Update a classification To update a classification, call the -[`template.start_update()`][start_update] API to start making changes to the +[`template.start_update()`][start_update] API to start making changes to the template, and then call the [`template.update_info(updates=new_updates)`][update_info] with the classification to change on the template. + ```python template = client.metadata_template('enterprise', 'securityClassification-6VMVochwUWo') new_updates = template.start_update() @@ -96,15 +94,15 @@ new_updates.edit_enum_option('Box__Security__Classification__Key', 'Sensitive', updated_template = template.update_info(updates=new_updates) ``` -Add classification to file --------------------------- +## Add classification to file -To add a classification to a file, call +To add a classification to a file, call [`file.metadata(scope='global', template='properties')`][set-metadata] with the name of the classification template, as well as the details of the classification to add to the file. + ```python classification = { 'Box__Security__Classification__Key': 'Sensitive', @@ -114,15 +112,15 @@ applied_metadata = client.file(file_id='11111').metadata(scope='enterprise', tem [set-metadata]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.metadata -Update classification on file ------------------------------ +## Update classification on file -To update a classification on a file, call +To update a classification on a file, call [`file.metadata(scope='global', template='properties')`][update-metadata] with the name of the classification template, as well as the details of the classification to add to the file. + ```python classification = { 'Box__Security__Classification__Key': 'Sensitive', @@ -132,40 +130,38 @@ applied_metadata = client.file(file_id='11111').metadata(scope='enterprise', tem [update-metadata]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.metadata -Get classification on file --------------------------- +## Get classification on file Retrieve the classification on a file by calling [`file.metadata(scope='global', template='properties').get()`](https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.get) on a file. + ```python metadata = client.file(file_id='11111').metadata(scope='enterprise', template='securityClassification-6VMVochwUWo').get() ``` -Remove classification from file -------------------------------- +## Remove classification from file A classification can be removed from a file by calling [`file.metadata(scope='global', template='properties').delete()`](https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.delete). + ```python client.file(file_id='11111').metadata(scope='securityClassification-6VMVochwUWo', template='myMetadata').delete() ``` +## Add classification to folder - -Add classification to folder --------------------------- - -To add a classification to a folder, call +To add a classification to a folder, call [`folder.metadata(scope='global', template='properties')`][set-metadata] with the name of the classification template, as well as the details of the classification to add to the folder. + ```python classification = { 'Box__Security__Classification__Key': 'Sensitive', @@ -175,15 +171,15 @@ applied_metadata = client.folder(folder_id='11111').metadata(scope='enterprise', [set-metadata]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.metadata -Update classification on folder ------------------------------ +## Update classification on folder -To update a classification on a folder, call +To update a classification on a folder, call [`folder.metadata(scope='global', template='properties')`][update-metadata] with the name of the classification template, as well as the details of the classification to add to the folder. + ```python classification = { 'Box__Security__Classification__Key': 'Sensitive', @@ -193,25 +189,25 @@ applied_metadata = client.folder(folder_id='11111').metadata(scope='enterprise', [update-metadata]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.metadata -Get classification on folder --------------------------- +## Get classification on folder Retrieve the classification on a folder by calling [`folder.metadata(scope='global', template='properties').get()`](https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.get) on a folder. + ```python metadata = client.folder(folder_id='11111').metadata(scope='enterprise', template='securityClassification-6VMVochwUWo').get() ``` -Remove classification from folder -------------------------------- +## Remove classification from folder A classification can be removed from a folder by calling [`folder.metadata(scope='global', template='properties').delete()`](https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.delete). + ```python client.folder(folder_id='11111').metadata(scope='securityClassification-6VMVochwUWo', template='myMetadata').delete() ``` diff --git a/docs/usage/collaboration.md b/docs/usage/collaboration.md index 871a7ea20..daaf87e1c 100644 --- a/docs/usage/collaboration.md +++ b/docs/usage/collaboration.md @@ -1,5 +1,4 @@ -Collaborations -============== +# Collaborations Collaborations are used to share folders between users or groups. They also define what permissions a user has for a folder. @@ -7,7 +6,6 @@ define what permissions a user has for a folder. - - [Add a Collaboration](#add-a-collaboration) - [Edit a Collaboration](#edit-a-collaboration) - [Remove a Collaboration](#remove-a-collaboration) @@ -18,16 +16,16 @@ define what permissions a user has for a folder. -Add a Collaboration -------------------- +## Add a Collaboration You can add a collaboration on a folder or a file by calling -[`item.collaborate(accessible_by, role, can_view_path=None, notify=None, fields=None)`][collaborate]. Pass the -[`User`][user_class] or [`Group`][group_class] to collaborate the item with as the `accessible_by` parameter. The -`role` parameter determines what permissions the collaborator will have on the folder. This method returns a +[`item.collaborate(accessible_by, role, can_view_path=None, notify=None, fields=None)`][collaborate]. Pass the +[`User`][user_class] or [`Group`][group_class] to collaborate the item with as the `accessible_by` parameter. The +`role` parameter determines what permissions the collaborator will have on the folder. This method returns a [`Collaboration`][collaboration_class] object representing the new collaboration on the item. + ```python from boxsdk.object.collaboration import CollaborationRole @@ -52,6 +50,7 @@ collaboration = client.folder(folder_id='22222').collaborate_with_login(email_of Or, you can invite a group using the group id + ```python from boxsdk.object.collaboration import CollaborationRole @@ -64,21 +63,21 @@ has_accepted = 'has' if collaboration.status == 'accepted' else 'has not' print(f'{collaborator.name} {has_accepted} accepted the collaboration to folder "{item.name}"') ``` -> __Note:__ The `can_view_path` parameter is currently only available for collaborations on folders. +> **Note:** The `can_view_path` parameter is currently only available for collaborations on folders. [collaborate]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.collaborate [user_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User [group_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.group.Group [collaboration_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration.Collaboration -Edit a Collaboration --------------------- +## Edit a Collaboration A collaboration can be edited by calling [`collaboration.update_info(*, data=None, role=None, status=None, **kwargs)`][update_info]. Note that `role` fields is always required when updating a collaboration. This method returns an updated [`Collaboration`][collaboration_class] object, leaving the original unmodified. + ```python from boxsdk.object.collaboration import CollaborationRole @@ -89,14 +88,14 @@ updated_collaboration = collaboration.update_info(data=collaboration_update) [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration.Collaboration.update_info -Remove a Collaboration ----------------------- +## Remove a Collaboration -A collaboration can be removed by calling [`collaboration.delete()`][delete]. This will generally cause the user or -group associated with the collaboration to lose access to the item. This method returns `True` to indicate that removal +A collaboration can be removed by calling [`collaboration.delete()`][delete]. This will generally cause the user or +group associated with the collaboration to lose access to the item. This method returns `True` to indicate that removal succeeded. + ```python collaboration_id = '1111' client.collaboration(collaboration_id).delete() @@ -104,28 +103,28 @@ client.collaboration(collaboration_id).delete() [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete -Get a Collaboration's Information ---------------------------------- +## Get a Collaboration's Information -To get information about a specific collaboration, call [`collaboration.get()`][get]. This method returns a new +To get information about a specific collaboration, call [`collaboration.get()`][get]. This method returns a new [`Collaboration`][collaboration_class] with fields populated by data from the API. + ```python collaboration = client.collaboration(collab_id='12345').get() ``` [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -List Collaborations on a Folder or File ----------------------------------------- +## List Collaborations on a Folder or File To retrieve all collaborations on a specified [`Folder`][folder_class] or [`File`][file_class], call -[`item.get_collaborations(limit=None, marker=None, fields=None)`][get_collaborations]. This method returns a +[`item.get_collaborations(limit=None, marker=None, fields=None)`][get_collaborations]. This method returns a `BoxObjectCollection` that you can use to iterate over all [`Collaboration`][collaboration_class] objects in the collection. + ```python collaborations = client.folder(folder_id='22222').get_collaborations() for collab in collaborations: @@ -134,6 +133,7 @@ for collab in collaborations: ``` + ```python collaborations = client.file(file_id='11111').get_collaborations() for collab in collaborations @@ -145,15 +145,15 @@ for collab in collaborations [file_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File [get_collaborations]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.get_collaborations -List Pending Collaborations ---------------------------- +## List Pending Collaborations To retrieve all pending collaborations for the current user, call -[`client.get_pending_collaborations(limit=None, offset=None, fields=None)`][get_pending_collaborations]. The user can -accept or reject these collaborations. This method returns a `BoxObjectCollection` that you +[`client.get_pending_collaborations(limit=None, offset=None, fields=None)`][get_pending_collaborations]. The user can +accept or reject these collaborations. This method returns a `BoxObjectCollection` that you can use to iterate over all pending [`Collaboration`][collaboration_class] objects in the collection. + ```python pending_collaborations = client.get_pending_collaborations() for pending_collaboration in pending_collaborations: @@ -162,14 +162,14 @@ for pending_collaboration in pending_collaborations: [get_pending_collaborations]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_pending_collaborations -Accept or Reject a Pending Collaboration ------------------------------------------ +## Accept or Reject a Pending Collaboration To accept or reject a pending collaboration, call [`collaboration.accept()`][accept] or -[`collaboration.reject()`][reject]. These methods both return the updated [`Collaboration`][collaboration_class] +[`collaboration.reject()`][reject]. These methods both return the updated [`Collaboration`][collaboration_class] object, leaving the original unmodified. + ```python accepted_collab = client.collaboration(collab_id='12345').accept() diff --git a/docs/usage/collaboration_allowlist.md b/docs/usage/collaboration_allowlist.md index 069418c5e..4683e9ebd 100644 --- a/docs/usage/collaboration_allowlist.md +++ b/docs/usage/collaboration_allowlist.md @@ -1,5 +1,4 @@ -Collaboration Allowlist -======================= +# Collaboration Allowlist The Collaboration Allowlist API allows you to manage a set of approved domains (i.e. a allowlist) that can collaborate with your enterprise. You can also manage whether the allowlisted domains are approved for outbound or inbound @@ -10,7 +9,6 @@ It is important to note that the collaboration allowlist functionality is only a - - [List Collaboration Allowlist Entries](#list-collaboration-allowlist-entries) - [Get Information for Collaboration Allowlist Entry](#get-information-for-collaboration-allowlist-entry) - [Allowlist a Domain for Collaboration](#allowlist-a-domain-for-collaboration) @@ -22,15 +20,15 @@ It is important to note that the collaboration allowlist functionality is only a -List Collaboration Allowlist Entries ------------------------------------- +## List Collaboration Allowlist Entries To retrieve a list of collaboration allowlist entries, call -[`collaboration_allowlist.get_entries(limit=None, marker=None, fields=None)`][get_entries]. This method returns a +[`collaboration_allowlist.get_entries(limit=None, marker=None, fields=None)`][get_entries]. This method returns a `BoxObjectCollection` which can iterate over the [`CollaborationAllowlistEntry`][entry_class] objects in the collection. + ```python allowlist_entries = client.collaboration_allowlist().get_entries() for entry in allowlist_entries: @@ -39,26 +37,25 @@ for entry in allowlist_entries: ``` [get_entries]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist.CollaborationAllowlist.get_entries -[entry_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist_entry.CollaborationAllowlistEntry +[entry_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist_entry.CollaborationAllowlistEntry -Get Information for Collaboration Allowlist Entry -------------------------------------------------- +## Get Information for Collaboration Allowlist Entry To get information about a collaboration allowlist entry, use [`collaboration_allowlist_entry.get(*, fields=None, headers=None, **kwargs)`][get]. This method returns a [`CollaborationAllowlistEntry`][entry_class] object with fields populated by data form the API. + ```python allowlist_entry = client.collaboration_allowlist_entry(entry_id='11111').get() ``` [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Allowlist a Domain for Collaboration ------------------------------------- +## Allowlist a Domain for Collaboration To allowlist a domain for collaboration, call [`collaboration_allowlist.add_domain(domain, direction)`][add_domain] with -the domain to allowlist and the direction(s) collaboration should be allowed in. This method returns a +the domain to allowlist and the direction(s) collaboration should be allowed in. This method returns a [`CollaborationAllowlistEntry`][entry_class] object representing the newly-allowlisted domain. You can determine the direction of the allowlist by passing in 'outbound', 'inbound', or 'both'. Outbound collaboration @@ -66,37 +63,38 @@ is defined as a user in your enterprise collaborating on content owned by someon collaboration is defined as a user outside of your enterprise collaborating on content owned by your enterprise. + ```python from boxsdk.object.collaboration_allowlist import AllowlistDirection domain = 'example.com' allowlist_entry = client.collaboration_allowlist().add_domain(domain, direction=AllowlistDirection.INBOUND) ``` -[add_domain]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist.CollaborationAllowlist.add_domain +[add_domain]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist.CollaborationAllowlist.add_domain -Remove a Domain from Allowlist ------------------------------- +## Remove a Domain from Allowlist -To remove a collaboration allowlisted domain, call [`collaboration_allowlist_entry.delete()`][delete]. This will remove -the domain from the allowlist, restricting collaboration to and from users in that domain. This method returns `True` +To remove a collaboration allowlisted domain, call [`collaboration_allowlist_entry.delete()`][delete]. This will remove +the domain from the allowlist, restricting collaboration to and from users in that domain. This method returns `True` to indicate that deletion was successful. + ```python client.collaboration_allowlist_entry(entry_id='11111').delete() ``` [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete -List Exempt Users ------------------ +## List Exempt Users To get all exempt users from the collaboration allowlist, call -[`collaboration_allowlist.get_exemptions(limit=None, marker=None, fields=None)`][get_exemptions]. This method returns +[`collaboration_allowlist.get_exemptions(limit=None, marker=None, fields=None)`][get_exemptions]. This method returns a `BoxObjectCollection` that allows you to iterate over each [`CollaborationAllowlistExemptTarget`][exemption_class] in the collection. + ```python exemptions = client.collaboration_allowlist().get_exemptions() for exemption in exemptions: @@ -106,27 +104,27 @@ for exemption in exemptions: [get_exemptions]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist.CollaborationAllowlist.get_exemptions [exemption_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist_exempt_target.CollaborationAllowlistExemptTarget -Get Exempt User Information ---------------------------- +## Get Exempt User Information To get information about an exempted user, call [`collaboration_allowlist_exempt_target.get(*, fields=None, headers=None, **kwargs)`][get]. This method will return a [`CollaborationAllowlistExemptTarget][exemption_class] with fields populated by data from the API. + ```python exemption_id = '11111' exemption = client.collaboration_allowlist_exempt_target(exemption_id).get() ``` -Exempt User from Allowlist --------------------------- +## Exempt User from Allowlist To exempt a user from the collaboration allowlist, call [`collaboration_allowlist.add_exemption(user)`][add_exemption] -with the [`User`][user_class] to exempt from the allowlist. This user will no longer be subject to the collaboration -allowlist, and will be permitted to collaborate with users from any other domain. This method returns a +with the [`User`][user_class] to exempt from the allowlist. This user will no longer be subject to the collaboration +allowlist, and will be permitted to collaborate with users from any other domain. This method returns a [`CollaborationAllowlistExemptTarget`][exemption_class] object representing the exempted user. + ```python user = client.user(user_id='11111') exemption = client.collaboration_allowlist().add_exemption(user) @@ -135,14 +133,14 @@ exemption = client.collaboration_allowlist().add_exemption(user) [add_exemption]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration_allowlist.CollaborationAllowlist.add_exemption [user_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User -Remove User Exemption ---------------------- +## Remove User Exemption To remove a user exemption from the collaboration allowlist, call -[`collaboration_allowlist_exempt_target.delete()`][delete]. This will remove the exemption and make the user subject to -the collaboration allowlist again. This method returns `True` to indicate that deletion was successful. +[`collaboration_allowlist_exempt_target.delete()`][delete]. This will remove the exemption and make the user subject to +the collaboration allowlist again. This method returns `True` to indicate that deletion was successful. + ```python client.collaboration_allowlist_exempt_target(exemption_id='22222').delete() ``` diff --git a/docs/usage/collections.md b/docs/usage/collections.md index 1e42cf932..beda5336c 100644 --- a/docs/usage/collections.md +++ b/docs/usage/collections.md @@ -1,5 +1,4 @@ -Collections -=========== +# Collections Collections allow users to mark specific files, folders and web links to make it easier to find them. @@ -13,14 +12,14 @@ Collections allow users to mark specific files, folders and web links to make it -Get a User's Collections ------------------------- +## Get a User's Collections To get all collections belonging to a user, call [`client.collections(limit=None, offset=0, fields=None)`][collections]. This method returns a `BoxObjectCollection` that you can use to iterate over all the [`Collection`][collection_class] objects in the set. + ```python collections = client.collections() for collection in collections: @@ -30,15 +29,15 @@ for collection in collections: [collections]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.collections [collection_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collection.Collection -Get the Items in a Collection ------------------------------ +## Get the Items in a Collection To retrieve a list of items contained in a collection, call -[`collection.get_items(limit=None, offset=0, fields=None)`][get_items]. This method returns a +[`collection.get_items(limit=None, offset=0, fields=None)`][get_items]. This method returns a `BoxObjectCollection` that you can use to iterate over all the [`BaseItem`][base_item_class] objects in the collection. [`BaseItem`][base_item_class] is a super class for files, folders and web links. + ```python items = client.collection(collection_id='12345').get_items() for item in items: @@ -48,14 +47,14 @@ for item in items: [get_items]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collection.Collection.get_items [base_item_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem -Add an Item to a Collection ----------------------- +## Add an Item to a Collection To add an [`BaseItem`][base_item_class] to a collection, call [`item.add_to_collection(collection)`][add_to_collection] with the -[`Collection`][collection_class] you want to add the item to. This method returns the updated [`BaseItem`][base_item_class] +[`Collection`][collection_class] you want to add the item to. This method returns the updated [`BaseItem`][base_item_class] object. + ```python collection = client.collection(collection_id='12345') updated_file = client.file(file_id='11111').add_to_collection(collection) @@ -64,14 +63,14 @@ print(f'File "{updated_file.name}" added to collection!') [add_to_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.add_to_collection -Remove an Item from a Collection ---------------------------- +## Remove an Item from a Collection -To remove an [`BaseItem`][base_item_class] from a collection, call +To remove an [`BaseItem`][base_item_class] from a collection, call [`item.remove_from_collection(collection)`][remove_from_collection] with the [`Collection`][collection_class] you want -to remove the item from. This method returns the updated [`BaseItem`][base_item_class] object. +to remove the item from. This method returns the updated [`BaseItem`][base_item_class] object. + ```python collection = client.collection(collection_id='12345') updated_file = client.file(file_id='11111').remove_from_collection(collection) diff --git a/docs/usage/comments.md b/docs/usage/comments.md index 04ef71499..d63ddfa46 100644 --- a/docs/usage/comments.md +++ b/docs/usage/comments.md @@ -1,12 +1,10 @@ -Comments -======== +# Comments Comment objects represent a user-created comment on a file. They can be added directly to a file. - - [Get Information About a Comment](#get-information-about-a-comment) - [Get the Comments on a File](#get-the-comments-on-a-file) - [Add a Comment to a File](#add-a-comment-to-a-file) @@ -16,8 +14,7 @@ Comment objects represent a user-created comment on a file. They can be added di -Get Information About a Comment -------------------------------- +## Get Information About a Comment To get a specific comment object, first call `[client.comment(comment_id)`][comment] to construct the appropriate [`Comment`][comment_class] object, and then call [`comment.get(*, fields=None, headers=None, **kwargs)`][get] to @@ -25,6 +22,7 @@ retrieve the data about the comment. The latter method returns a new [`Comment`] populated by data from the API, leaving the original unmodified. + ```python comment = client.comment(comment_id='55555').get() print(f'The comment says "{comment.message}"') @@ -34,14 +32,14 @@ print(f'The comment says "{comment.message}"') [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get [comment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.comment.Comment -Get the Comments on a File --------------------------- +## Get the Comments on a File To retrieve the comment left on a file, call [`file.get_comments(limit=None, offset=0, fields=None)`][get_comments]. This method returns a `BoxObjectCollection` that you can use to iterate over all the [`Comment`][comment_class] objects in the set. + ```python comments = client.file(file_id='11111').get_comments() for comment in comments: @@ -50,20 +48,21 @@ for comment in comments: [get_comments]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_comments -Add a Comment to a File ------------------------ +## Add a Comment to a File To leave a comment on a file, call [`file.add_comment(message)`][add_comment] with the message to leave in the comment. + ```python comment = client.file(file_id='11111').add_comment('When should I have this done by?') ``` -You can at-mention other users by adding special tags within the message, in the format `@[USER_ID:USER_NAME]`. For +You can at-mention other users by adding special tags within the message, in the format `@[USER_ID:USER_NAME]`. For example, to at-mention John Doe, whose user ID is `"33333"`: `@[33333:John Doe]`. + ```python comment = client.file(file_id='11111').add_comment('Hey @[44444:boss], when should I have this done by?') ``` @@ -72,20 +71,21 @@ This method returns a [`Comment`][comment_class] object representing the newly-c [add_comment]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.add_comment -Reply to a Comment ------------------- +## Reply to a Comment To reply to a comment, call [`comment.reply(message)`][reply] with the message to leave in the comment. + ```python reply_comment = client.comment(comment_id='12345').reply('If possible, please finish this by the end of the week!') ``` -You can at-mention other users by adding special tags within the message, in the format `@[USER_ID:USER_NAME]`. For +You can at-mention other users by adding special tags within the message, in the format `@[USER_ID:USER_NAME]`. For example, to at-mention John Doe, whose user ID is `"33333"`: `@[33333:John Doe]`. + ```python reply_comment = client.comment(comment_id='12345').reply('@[33333:John Doe], if possible, please finish this by the end of the week!') ``` @@ -94,28 +94,28 @@ This method returns a [`Comment`][comment_class] object representing the newly-c [reply]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.comment.Comment.reply -Edit a Comment --------------- +## Edit a Comment To edit a comment and change its message, call [`comment.edit(message)`][edit] with the message to leave in the comment. -You can at-mention other users by adding special tags within the message, in the format `@[USER_ID:USER_NAME]`. For -example, to at-mention John Doe, whose user ID is `"33333"`: `@[33333:John Doe]`. This method returns an updated +You can at-mention other users by adding special tags within the message, in the format `@[USER_ID:USER_NAME]`. For +example, to at-mention John Doe, whose user ID is `"33333"`: `@[33333:John Doe]`. This method returns an updated [`Comment`][comment_class] object, leaving the original unmodified. + ```python edited_comment = client.comment(comment_id='98765').edit('If possible, please finish this by Friday!') ``` [edit]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.comment.Comment.edit -Delete a Comment ----------------- +## Delete a Comment -To delete a comment, call [`comment.delete()`][delete]. This will remove the comment from the file. This method +To delete a comment, call [`comment.delete()`][delete]. This will remove the comment from the file. This method returns `True` to indicate that the deletion succeeded. + ```python client.comment(comment_id='12345').delete() ``` diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md index c12848d14..223f3377e 100644 --- a/docs/usage/configuration.md +++ b/docs/usage/configuration.md @@ -1,5 +1,4 @@ -Configuration -============= +# Configuration The Python SDK has helpful custom config that you can set for a variety of use cases. @@ -17,8 +16,7 @@ The Python SDK has helpful custom config that you can set for a variety of use c -Proxy ------ +## Proxy ### Unauthenticated Proxy @@ -41,10 +39,10 @@ Proxy.AUTH = { } ``` -URLs configuration ------------------- +## URLs configuration ### Base URL + The default base URL used for making API calls to Box can be changed by setting the value of the `API.BASE_API_URL` field. ```python @@ -53,7 +51,8 @@ API.BASE_API_URL = 'https://new-base-url.com' ``` ### OAUTH2 URLs -The default URLs used to authorize a user and obtain OAuth2 authorization tokens can be modified by overwriting + +The default URLs used to authorize a user and obtain OAuth2 authorization tokens can be modified by overwriting `API.OAUTH2_API_URL` and `API.OAUTH2_AUTHORIZE_URL` constants. ```python @@ -63,6 +62,7 @@ API.OAUTH2_AUTHORIZE_URL = 'https://my-company.com/authorize' ``` ### Upload URL + The default URL used when uploading files to Box can be changed by assigning a new value to the `API.UPLOAD_URL` field. If this variable is ever changed from default value, the SDK will alwayse use this URL to upload files to Box, even if `use_upload_session_urls` is set to `True` while creating an upload session for a chunked upload. @@ -72,11 +72,11 @@ from boxsdk.config import API API.UPLOAD_URL = 'https://my-company-upload-url.com' ``` -Max retry attmepts ------------------- +## Max retry attmepts The default maximum number of retries in case of failed API call is 5 (usually 202, 429 and >= 500 response codes are retried). To change this number you can set `API.MAX_RETRY_ATTEMPTS` field. + ```python from boxsdk.config import API API.MAX_RETRY_ATTEMPTS = 6 diff --git a/docs/usage/device_pin.md b/docs/usage/device_pin.md index 08628b5e2..cf2966fe6 100644 --- a/docs/usage/device_pin.md +++ b/docs/usage/device_pin.md @@ -1,5 +1,4 @@ -Device Pins -=========== +# Device Pins Device pinning is a feature that allows enterprise admins to pin their user’s corporate-managed Box account to a particular mobile device or Box Sync client. @@ -7,22 +6,21 @@ corporate-managed Box account to a particular mobile device or Box Sync client. - - [List Enterprise Device Pins](#list-enterprise-device-pins) - [Get Device Pin Information](#get-device-pin-information) - [Delete Device Pin](#delete-device-pin) -List Enterprise Device Pins ---------------------------- +## List Enterprise Device Pins To retrieve all device pins for an enterprise, call [`client.device_pinners(enterprise=None, limit=None, marker=None, direction=None, fields=None)`][device_pinners]. -If an `enterprise` is not specified, this defaults to the current enterprise. This method returns a +If an `enterprise` is not specified, this defaults to the current enterprise. This method returns a `BoxObjectCollection` that allows you to iterate over the [`DevicePinner`][device_pin_class] objects in the collection. + ```python device_pins = client.device_pinners() for pin in device_pins: @@ -32,13 +30,13 @@ for pin in device_pins: [device_pinners]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.device_pinners [device_pin_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.device_pinner.DevicePinner -Get Device Pin Information --------------------------- +## Get Device Pin Information To get information about a specific device pin, call [`device_pinner.get(*, fields=None, headers=None, **kwargs)`][get]. This method returns a new [`DevicePinner`][device_pin_class] object with fields populated by data from the API. + ```python device_pin_id = '1111' device_pin = client.device_pinner(device_pin_id).get() @@ -47,13 +45,13 @@ print(f'{pin.product_name} device for {pin.owned_by.name} is pinned') [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Delete Device Pin ------------------ +## Delete Device Pin -To delete a specific device pin, call [`device_pinner.delete()`][delete]. This method returns `True` to indicate that +To delete a specific device pin, call [`device_pinner.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python device_pin_id = '1111' client.device_pin(device_pin_id).delete() diff --git a/docs/usage/events.md b/docs/usage/events.md index 34946205c..097ab0fdf 100644 --- a/docs/usage/events.md +++ b/docs/usage/events.md @@ -1,5 +1,4 @@ -Events -====== +# Events It is possible to poll the Box API for events, in order to get information about activity within Box as it happens. @@ -9,7 +8,6 @@ the events in an enterprise. - - [User Events](#user-events) - [Listening to the Event Stream](#listening-to-the-event-stream) - [Get the Current Stream Position](#get-the-current-stream-position) @@ -19,8 +17,7 @@ the events in an enterprise. -User Events ------------ +## User Events The Box API provides an events endpoint that utilizes long-polling to send events in real-time. The SDK provides a generator that automatically handles long-polling and deduplicating events. @@ -29,10 +26,11 @@ generator that automatically handles long-polling and deduplicating events. To automatically receive events as they happen, call [`events.generate_events_with_long_polling(stream_position=None, stream_type=UserEventsStreamType.ALL)`][generator] and iterate over -the results. By default, this will start listening for events from the current time onward; to get all available events, -pass a `stream_position` of `0`. The generator yields [`Event`][event_class] objects representing each event. +the results. By default, this will start listening for events from the current time onward; to get all available events, +pass a `stream_position` of `0`. The generator yields [`Event`][event_class] objects representing each event. + ```python events = client.events().generate_events_with_long_polling() for event in events: @@ -58,12 +56,13 @@ print(f'The current stream position is {stream_position}') ### Get Events Manually To manually retrieve a set of events, call -[`events.get_events(limit=100, stream_position=0, stream_type=UserEventsStreamType.ALL)`][get_events]. By default, this +[`events.get_events(limit=100, stream_position=0, stream_type=UserEventsStreamType.ALL)`][get_events]. By default, this will fetch the first available events chronologically; you can pass a specific `stream_position` to get events from a -particular time. This method returns a `dict` with the relevant [`Event`][event_class] objects in a `list` under the +particular time. This method returns a `dict` with the relevant [`Event`][event_class] objects in a `list` under the `entries` key and the next stream position value under the `next_stream_position` key. + ```python stream_position = 0 events = client.events().get_events(stream_position=stream_position) @@ -74,8 +73,7 @@ for event in events['entries']: [get_events]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.events.Events.get_events -Enterprise Events ------------------ +## Enterprise Events Currently, the SDK only provides a manual interface for retrieving Enterprise (or Admin) Events. @@ -83,8 +81,8 @@ Currently, the SDK only provides a manual interface for retrieving Enterprise (o To manually retrieve a set of admin events, call [`events.get_events(limit=100, stream_position=0, stream_type=UserEventsStreamType.ALL)`][get_events] with `stream_type` -set to `EnterpriseEventsStreamType.ADMIN_LOGS`. By default, this will fetch the first available events chronologically; -you can pass a specific `stream_position` to get events from a particular time. This method returns a `dict` with the +set to `EnterpriseEventsStreamType.ADMIN_LOGS`. By default, this will fetch the first available events chronologically; +you can pass a specific `stream_position` to get events from a particular time. This method returns a `dict` with the relevant [`Event`][event_class] objects in a `list` under the `entries` key and the next stream position value under the `next_stream_position` key. @@ -105,21 +103,23 @@ If `limit` param is set to None, then default API value (limit=100) will be used Live monitoring example + ```python events = client.events() .get_admin_events_streaming() for event in events['entries']: - print(f'Got {event.event_type} event that occurred at {event.created_at}') + print(f'Got {event.event_type} event that occurred at {event.created_at}') ``` Addditionally, a list of event types can be passed along to filter down the returned events. + ```python events = client.events() .get_admin_events_streaming(event_types=['ITEM_CREATE']) for event in events['entries']: - print(f'Got {event.event_type} event that occurred at {event.created_at}') + print(f'Got {event.event_type} event that occurred at {event.created_at}') ``` When using historical querying you can specify before and after a certain datetime and the types of events to retrieve with the `event_type` by calling @@ -131,21 +131,23 @@ This method returns a `dict` with the relevant [`Event`][event_class] objects in `entries` key and the next stream position value under the `next_stream_position` key. + ```python events = client.events() .get_admin_events(created_after='2019-07-01T22:02:24-07:00') for event in events['entries']: - print(f'Got {event.event_type} event that occurred at {event.created_at}') + print(f'Got {event.event_type} event that occurred at {event.created_at}') ``` Addditionally, a list of event types can be passed along to filter down the returned events. + ```python events = client.events() .get_admin_events(created_after='2019-07-01T22:02:24-07:00', event_types=['ITEM_CREATE']) for event in events['entries']: - print(f'Got {event.event_type} event that occurred at {event.created_at}') + print(f'Got {event.event_type} event that occurred at {event.created_at}') ``` [admin_events_details]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.events.Events.get_admin_events diff --git a/docs/usage/file_request.md b/docs/usage/file_request.md index 8ecdb0cc3..b6d9d2456 100644 --- a/docs/usage/file_request.md +++ b/docs/usage/file_request.md @@ -1,5 +1,4 @@ -File Requests -============= +# File Requests File request objects represent a file request associated with a folder. @@ -13,13 +12,13 @@ File request objects represent a file request associated with a folder. -Get a File Request's Information ------------------------- +## Get a File Request's Information -To get a file request object, first call [`client.file_request(file_request_id)`][file_request] to construct the appropriate [`FileRequest`][file_request_class] object, and then calling [`file_request.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`FileRequest`][file_request_class] object populated with data +To get a file request object, first call [`client.file_request(file_request_id)`][file_request] to construct the appropriate [`FileRequest`][file_request_class] object, and then calling [`file_request.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`FileRequest`][file_request_class] object populated with data from the API, leaving the original object unmodified. + ```python file_request = client.file_request(file_request_id='123456').get() print(f'File request {file_request.id} on folder {file_request.folder.name}') @@ -29,13 +28,12 @@ print(f'File request {file_request.id} on folder {file_request.folder.name}') [file_request_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file_request.FileRequest [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get - -Copy a File Request's Information ---------------------------- +## Copy a File Request's Information To copy a file request, first call [`client.file_request(file_request_id)`][file_request] to construct the appropriate [`FileRequest`][file_request_class] object, and then calling [`file_request.copy(folder, description=None, title=None, expires_at=None, require_description=None, require_email=None, status=None)`][copy]. It will return the [`FileRequest`][file_request_class] object populated with data new created file request from the API. + ```python file_request = client.file_request(file_request_id='123456') folder = client.folder(folder_id='123456789') @@ -45,17 +43,17 @@ new_file_request = file_request.copy(folder=folder, title="Copied file request") [file_request_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file_request.FileRequest [copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file_request.FileRequest.copy -Update a File Request's Information ---------------------------- +## Update a File Request's Information To update a file request object, call [`file_request.update_info(data=file_request_update)`][update_info] with a `dict` of properties to update on the file request. This method returns a newly updated [`FileRequest`][file_request_class] object, leaving the original object unmodified. + ```python from boxsdk.object.file_request import StatusState update_data = { - "description": 'Updated description', + "description": 'Updated description', "is_email_required": True, "status": StatusState.ACTIVE } @@ -65,15 +63,15 @@ file_request.update_info(data=update_data) [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info [file_request_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file_request.FileRequest -Delete a File Request -------------- +## Delete a File Request To delete a file request, call [`file_request.delete()`][delete], it deletes a file request permanently. + ```python file_request = client.file_request(file_request_id='123456') file_request.delete() ``` -[delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete \ No newline at end of file +[delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete diff --git a/docs/usage/files.md b/docs/usage/files.md index a4f67de87..ea96c3eff 100644 --- a/docs/usage/files.md +++ b/docs/usage/files.md @@ -1,5 +1,4 @@ -Files -===== +# Files File objects represent individual files in Box. They can be used to download a file's contents, upload new versions, and perform other common file operations @@ -58,15 +57,15 @@ file's contents, upload new versions, and perform other common file operations -Get a File's Information ------------------------- +## Get a File's Information Calling [`file.get(*, fields=None, etag=None, **kwargs)`][get_info] on a [`File`][file_class] retrieves information -about the file from the API. This method returns a new [`File`][file_class] object populated with the information retrieved. +about the file from the API. This method returns a new [`File`][file_class] object populated with the information retrieved. You can specify an `Iterable` of fields to retrieve from the API in the `fields` parameter. + ```python file_id = '11111' file_info = client.file(file_id).get() @@ -76,31 +75,31 @@ print(f'File "{file_info.name}" has a size of {file_info.size} bytes') [get_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get [file_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File -Update a File's Information ---------------------------- +## Update a File's Information To update fields on the [`File`][file_class] object, call [`file.update_info(data=data_to_update)`][update_info] with -a `dict` of fields to update. This method returns the updated [`File`][file_class] object, leaving the original it +a `dict` of fields to update. This method returns the updated [`File`][file_class] object, leaving the original it was called on unmodified. + ```python file_id = '11111' updated_file = client.file(file_id).update_info(data={'description': 'My file'}) ``` -Download a File ---------------- +## Download a File A file can be downloaded in two ways: by returning the entire contents of the file as `bytes` or by providing an output -stream to which the contents of the file will be written. For both methods, you can optionally download a specific -version of the file by passing the desired [`FileVersion`][file_version_class] in the `file_version` parameter. You may +stream to which the contents of the file will be written. For both methods, you can optionally download a specific +version of the file by passing the desired [`FileVersion`][file_version_class] in the `file_version` parameter. You may also wish to download only a certain chunk of the file by passing a tuple of byte offsets via the `byte_range` parameter — the lower and upper bounds you wish to download. -To get the entire contents of the file as `bytes`, call [`file.content(file_version=None, byte_range=None)`][content]. +To get the entire contents of the file as `bytes`, call [`file.content(file_version=None, byte_range=None)`][content]. + ```python file_id = '11111' file_content = client.file(file_id).content() @@ -109,6 +108,7 @@ file_content = client.file(file_id).content() For users with premium accounts, previous versions of a file can be downloaded. + ```python file_id = '11111' file_version = client.file_version('12345') @@ -137,15 +137,15 @@ with open('file.pdf', 'wb') as output_file: [content]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.content [download_to]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.download_to -Get Download URL ----------------- +## Get Download URL To get a download URL suitable for passing to a web browser or other application, which will allow someone to download -the file, call [`file.get_download_url(file_version=None)`][get_download_url]. The will return a `unicode` string -containing the file's download URL. You can optionally pass a [`FileVersion`][file_version_class] via the +the file, call [`file.get_download_url(file_version=None)`][get_download_url]. The will return a `unicode` string +containing the file's download URL. You can optionally pass a [`FileVersion`][file_version_class] via the `file_version` parameter to get a download URL for a specific version of the file. + ```python file_id = '11111' download_url = client.file(file_id).get_download_url() @@ -154,19 +154,19 @@ print(f'The file\'s download URL is: {download_url}') [get_download_url]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_download_url -Upload a File -------------- +## Upload a File Files are uploaded to a folder in one of two ways: by providing a path to a file on disk, or via a readable stream containing the file contents. To upload a file from a path on disk, call the [`folder.upload(file_path, file_name=None, file_description=None,preflight_check=False, preflight_expected_size=0)`][upload] method -on the [`Folder`][folder_class] you want to upload the file into. By default, the file uploaded to Box will have the +on the [`Folder`][folder_class] you want to upload the file into. By default, the file uploaded to Box will have the same file name as the one on disk; you can override this by passing a different name in the `file_name` parameter. You can, optionally, also choose to set a file description upon upload by using the `file_description` parameter. This method returns a [`File`][file_class] object representing the newly-uploaded file. + ```python folder_id = '22222' new_file = client.folder(folder_id).upload('/home/me/document.pdf') @@ -175,7 +175,7 @@ print(f'File "{new_file.name}" uploaded to Box with file ID {new_file.id}') To upload a file from a readable stream, call [`folder.upload_stream(file_stream, file_name, file_description=None, preflight_check=False, preflight_expected_size=0)`][upload_stream] -with the stream and a name for the file. This method returns a [`File`][file_class] object representing the +with the stream and a name for the file. This method returns a [`File`][file_class] object representing the newly-uploaded file. ```python @@ -187,35 +187,36 @@ new_file = client.folder(folder_id).upload_stream(stream, file_name) print(f'File "{new_file.name}" uploaded to Box with file ID {new_file.id}') ``` ----- +--- + **NOTE:** -Both methods `folder.upload()` and `folder.upload_stream()` include the `stream_file_content` parameter, +Both methods `folder.upload()` and `folder.upload_stream()` include the `stream_file_content` parameter, which controls how the file content is uploaded. -If you are uploading a large file, you may want to stream the request to avoid excessive memory usage. +If you are uploading a large file, you may want to stream the request to avoid excessive memory usage. According to `requests'` library [docs][request_docs], by default, the `requests` library does not support streaming uploads, and all the data must be read into memory before being sent to the server. However, the `requests-toolbelt` package includes a `MultipartEncoder` class, which enables file uploads without loading the entire file into memory. This approach is the default in the Box Python SDK. -That said, handling 307 Temporary Redirects presents a challenge with streamed file uploads. -307 redirect requires that both the request method and body remain unchanged. -This can be problematic when uploading a file stream because the stream will already be exhausted when the redirect occurs. +That said, handling 307 Temporary Redirects presents a challenge with streamed file uploads. +307 redirect requires that both the request method and body remain unchanged. +This can be problematic when uploading a file stream because the stream will already be exhausted when the redirect occurs. To address this issue, the `stream_file_content` parameter has been introduced in upload methods. This allows you to choose between: - - Streaming the file (`stream_file_content=True`): Optimizes memory usage but may cause issues with redirects. - - - Using the default `requests'` library behavior (`stream_file_content=False`): Ensures the file can be re-read if a - redirect occurs but may consume more memory. This is especially important when working with proxy servers. + +- Streaming the file (`stream_file_content=True`): Optimizes memory usage but may cause issues with redirects. + +- Using the default `requests'` library behavior (`stream_file_content=False`): Ensures the file can be re-read if a + redirect occurs but may consume more memory. This is especially important when working with proxy servers. [folder_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder [upload]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.upload [upload_stream]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.upload_stream [request_docs]: https://docs.python-requests.org/en/latest/user/quickstart/#post-a-multipart-encoded-file -Chunked Upload --------------- +## Chunked Upload For large files or in cases where the network connection is less reliable, you may want to upload the file in parts. This allows a single part to fail @@ -223,7 +224,7 @@ without aborting the entire upload, and failed parts can then be retried. Since box-python-sdk 3.11.0 release, by default the SDK uses upload urls provided in response when creating a new upload session. This allowes to always upload your content to the closest Box data center and -can significantly improve upload speed. You can always disable this feature and always use base upload url by +can significantly improve upload speed. You can always disable this feature and always use base upload url by setting `use_upload_session_urls` flag to `False` when creating upload session. ### Automatic Uploader @@ -244,12 +245,13 @@ Then call [`folder.get_chunked_uploader(file_path, rename_file=False, use_upload to retrieve a [`ChunkedUploader`][chunked_uploader_class] object. Setting `use_upload_session_urls` to `True` inilializes the uploader that utlizies urls returned by the `Create Upload Session` endpoint response unless a custom API.UPLOAD_URL was set in the config. Setting `use_upload_session_urls` to `False` inilializes the uploader that uses always base upload urls. -Calling the method [`chunked_upload.start()`][start] will kick off the chunked upload process and return the [File][file_class] +Calling the method [`chunked_upload.start()`][start] will kick off the chunked upload process and return the [File][file_class] object that was uploaded. + ```python -# uploads large file to a root folder +# uploads large file to a root folder chunked_uploader = client.folder('0').get_chunked_uploader(file_path='/path/to/file.txt', file_name='new_name.txt') uploaded_file = chunked_uploader.start() print(f'File "{uploaded_file.name}" uploaded to Box with file ID {uploaded_file.id}') @@ -257,7 +259,7 @@ print(f'File "{uploaded_file.name}" uploaded to Box with file ID {uploaded_file. You can also upload file stream by creating a [`UploadSession`][upload_session_class] first. This can be done by calling [`folder.create_upload_session(file_size, file_name=None, use_upload_session_urls=True)`][create_upload_session] method. -`use_upload_session_urls` flag is used to determine if the upload session should use urls returned by +`use_upload_session_urls` flag is used to determine if the upload session should use urls returned by the `Create Upload Session` endpoint or should it always use base upload urls. Then you can call method [`upload_session.get_chunked_uploader_for_stream(content_stream, file_size)`][get_chunked_uploader_for_stream]. @@ -279,8 +281,9 @@ to retrieve a [`ChunkedUploader`][chunked_uploader_class] object. Calling the me will kick off the chunked upload process and return the updated [File][file_class]. + ```python -# uploads new large file version +# uploads new large file version chunked_uploader = client.file('existing_big_file_id').get_chunked_uploader(file_path='/path/to/file') uploaded_file = chunked_uploader.start() print(f'File "{uploaded_file.name}" uploaded to Box with file ID {uploaded_file.id}') @@ -289,11 +292,12 @@ print(f'File "{uploaded_file.name}" uploaded to Box with file ID {uploaded_file. #### Preflight check before upload -To check if a file can be uploaded with given name to a specific folder call +To check if a file can be uploaded with given name to a specific folder call [`folder.preflight_check(size, name)`][preflight_check]. If the check did not pass, this method will raise an exception including some details on why it did not pass. + ```python file_name = 'large_file.mp4' test_file_path = '/path/to/large_file.mp4' @@ -401,6 +405,7 @@ the `Create Upload Session` endpoint or should it always use base upload urls. T [`UploadSession`][upload_session_class] object representing the created upload session. + ```python file_size = 26000000 upload_session = client.file('11111').create_upload_session(file_size) @@ -416,9 +421,10 @@ To create an upload session for uploading a new large file, call [`folder.create_upload_session(file_size, file_name, use_upload_session_urls=True)`][create_upload_session] with the size and filename of the file to be uploaded. `use_upload_session_urls` flag is used to determine if the upload session should use urls returned by the `Create Upload Session` endpoint or should it always use base upload urls. -This method returns an [`UploadSession`][upload_session_class] object representing the created upload session. +This method returns an [`UploadSession`][upload_session_class] object representing the created upload session. + ```python file_size = 26000000 file_name = 'test_file.pdf' @@ -433,13 +439,14 @@ print(f'Created upload session {upload_session.id} with chunk size of {upload_se To upload a part of the file to this session, call [`upload_session.upload_part_bytes(part_bytes, offset, total_size, part_content_sha1=None)`][upload_part_bytes] with the `bytes` to be uploaded, the byte offset within the file (which should be a multiple of the upload session -`part_size`), and the total size of the file being uploaded. This method returns a `dict` for the part record; these +`part_size`), and the total size of the file being uploaded. This method returns a `dict` for the part record; these records should be kept for the commit operation. -> __Note:__ The number of bytes uploaded for each part must be exactly `upload_sesion.part_size`, except for the last +> **Note:** The number of bytes uploaded for each part must be exactly `upload_sesion.part_size`, except for the last > part (which just includes however many bytes are left in the file). + ```python upload_session = client.upload_session('11493C07ED3EABB6E59874D3A1EF3581') offset = upload_session.part_size * 3 @@ -455,11 +462,12 @@ print(f'Successfully uploaded part ID {part["part_id"]}') After uploading all parts of the file, commit the upload session to Box by calling [`upload_session.commit(content_sha1, parts=None, file_attributes=None, etag=None)`][commit] with the SHA1 hash of the -entire file. For best consistency guarantees, you should also pass an `Iterable` of the parts `dict`s via the `parts` -parameter; otherwise, the list of parts will be retrieved from the API. You may also pass a `dict` of `file_attributes` +entire file. For best consistency guarantees, you should also pass an `Iterable` of the parts `dict`s via the `parts` +parameter; otherwise, the list of parts will be retrieved from the API. You may also pass a `dict` of `file_attributes` to set on the new file. + ```python import hashlib @@ -479,10 +487,11 @@ print(f'Successfully uploaded file {uploaded_file.id} with description {uploaded #### Abort Upload Session -To abort a chunked upload and lose all uploaded file parts, call [`upload_session.abort()`][abort]. This method returns +To abort a chunked upload and lose all uploaded file parts, call [`upload_session.abort()`][abort]. This method returns `True` to indicate that the deletion succeeded. + ```python client.upload_session('11493C07ED3EABB6E59874D3A1EF3581').abort() print('Upload was successfully canceled') @@ -496,6 +505,7 @@ To return the list of parts uploaded so far, call [`upload_session.get_parts(lim This method returns a `BoxObjectCollection` that allows you to iterate over the part `dict`s in the collection. + ```python parts = client.upload_session('11493C07ED3EABB6E59874D3A1EF3581').get_parts() for part in parts: @@ -504,12 +514,11 @@ for part in parts: [get_parts]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.upload_session.UploadSession.get_parts -Move a File ------------ +## Move a File To move a file from one folder into another, call [`file.move(parent_folder, name=None)`][move] with the destination -folder to move the file into. You can optionally provide a `name` parameter to automatically rename the file in case -of a name conflict in the destination folder. This method returns the updated [`File`][file_class] object in the new +folder to move the file into. You can optionally provide a `name` parameter to automatically rename the file in case +of a name conflict in the destination folder. This method returns the updated [`File`][file_class] object in the new folder. ```python @@ -525,14 +534,14 @@ print(f'File "{moved_file.name}" has been moved into folder "{moved_file.parent. [move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.move -Copy a File ------------ +## Copy a File A file can be copied to a new folder by calling [`file.copy(*, parent_folder, name=None, file_version=None, **_kwargs)`][copy] with the destination folder and an optional new name for the file in case there is a name conflict in the destination -folder. This method returns a [`File`][file_class] object representing the copy of the file in the destination folder. +folder. This method returns a [`File`][file_class] object representing the copy of the file in the destination folder. + ```python file_id = '11111' destination_folder_id = '44444' @@ -546,11 +555,10 @@ print(f'File "{file_copy.name}" has been copied into folder "{file_copy.parent.n [copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.copy -Rename a File ------------ +## Rename a File A file can be renamed by calling [`file.rename(name)`][rename]. This method returns the updated -[`File`][file_class] object with a new name. Remeber to provide also extension of the file along with the new name. +[`File`][file_class] object with a new name. Remeber to provide also extension of the file along with the new name. ```python file = client.file(file_id='11111') @@ -561,29 +569,29 @@ print(f'File was renamed to "{renamed_file.name}"') [rename]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.rename -Delete a File -------------- +## Delete a File -Calling the [`file.delete()`][delete] method will delete the file. Depending on enterprise settings, this will either move -the file to the user's trash or permanently delete the file. This method returns `True` to signify that the deletion +Calling the [`file.delete()`][delete] method will delete the file. Depending on enterprise settings, this will either move +the file to the user's trash or permanently delete the file. This method returns `True` to signify that the deletion was successful. + ```python client.file(file_id='11111').delete() ``` [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.delete -Get Previous Versions of a File -------------------------------- +## Get Previous Versions of a File Previous versions of a file can be retrieved with the -[`file.get_previous_versions(limit=None, offset=None, fields=None)`][get_previous_versions] method. This method returns +[`file.get_previous_versions(limit=None, offset=None, fields=None)`][get_previous_versions] method. This method returns a [`BoxObjectCollection`][box_object_collection] that can iterate over the [`FileVersion`][file_version_class] objects in the collection. + ```python file_id = '11111' @@ -595,17 +603,17 @@ for version in file_versions: [get_previous_versions]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_previous_versions [box_object_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.pagination.html#boxsdk.pagination.box_object_collection.BoxObjectCollection -Upload a New Version of a File ------------------------------- +## Upload a New Version of a File New versions of a file can be uploaded in one of two ways: by providing a path to a file on disk, or via a readable stream containing the file contents. To upload a new file version from a path on disk, call the [`file.update_contents(file_path, etag=None, preflight_check=False, preflight_expected_size=0)`][update_contents] -method. This method returns a [`File`][file_class] object representing the updated file. +method. This method returns a [`File`][file_class] object representing the updated file. + ```python file_id = '11111' file_path = '/path/to/file.pdf' @@ -616,7 +624,7 @@ print(f'File "{updated_file.name}" has been updated') To upload a file version from a readable stream, call [`file.update_contents_with_stream(file_stream, etag=None, preflight_check=False, preflight_expected_size=0)`][update_contents_with_stream] -with the stream. This method returns a [`File`][file_class] object representing the +with the stream. This method returns a [`File`][file_class] object representing the newly-uploaded file. ```python @@ -630,15 +638,15 @@ print(f'File "{updated_file.name}" has been updated') [update_contents]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.update_contents [update_contents_with_stream]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.update_contents_with_stream -Promote a Previous Version of a File ------------------------------------- +## Promote a Previous Version of a File A previous version of a file can be promoted by calling the [`file.promote_version(file_version)`][promote_version] -method to become the current version of the file with the [`FileVersion`][file_version_class] to promote. This create a -copy of the old file version and puts it on the top of the versions stack. This method returns the new copy +method to become the current version of the file with the [`FileVersion`][file_version_class] to promote. This create a +copy of the old file version and puts it on the top of the versions stack. This method returns the new copy [`FileVersion`][file_version_class] object. + ```python file_id = '11111' file_version_id = '12345' @@ -651,13 +659,13 @@ print(f'Version {file_version_id} promoted; new version {new_version.id} created [promote_version]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.promote_version -Delete a Previous Version of a File ------------------------------------ +## Delete a Previous Version of a File A version of a file can be deleted and moved to the trash by calling [`file.delete_version(file_version, etag=None)`][delete_version] with the [`FileVersion`] to delete. + ```python file_id = '11111' version_id = '12345' @@ -668,16 +676,15 @@ client.file(file_id).delete_version(version_to_delete) [delete_version]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.delete_version -Lock a File ------------ +## Lock a File -A locked file cannot be modified by any other user until it is unlocked. This is useful if you want to "check out" a +A locked file cannot be modified by any other user until it is unlocked. This is useful if you want to "check out" a file while you're working on it, to ensure that other collaborators do not make changes while your changes are in progress. -To lock a file, call [`file.lock(prevent_download=False, expire_time=None)`][lock]. You can optionally prevent other -users from downloading the file while it is locked by passing `True` for the `prevent_download` parameter. You can also -set an expiration time for the lock, which will automatically release the lock at the specified time. The expiration +To lock a file, call [`file.lock(prevent_download=False, expire_time=None)`][lock]. You can optionally prevent other +users from downloading the file while it is locked by passing `True` for the `prevent_download` parameter. You can also +set an expiration time for the lock, which will automatically release the lock at the specified time. The expiration time is formatted as an [RFC3339 datetime][rfc3339]. This method returns the updated [`File`][file_class] object. @@ -692,10 +699,9 @@ print(f'File "{updated_file.name}" has been locked!') [lock]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.lock [rfc3339]: https://tools.ietf.org/html/rfc3339#section-5.8 -Unlock a File -------------- +## Unlock a File -A locked file can be unlocked by calling [`file.unlock()`][unlock]. This method returns the updated +A locked file can be unlocked by calling [`file.unlock()`][unlock]. This method returns the updated [`File`][file_class] object. ```python @@ -707,14 +713,14 @@ print(f'File "{updated_file.name}" has been unlocked!') [unlock]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.unlock -Create a Shared Link Download URL --------------------- +## Create a Shared Link Download URL A shared link for a file can be generated by calling [`file.get_shared_link_download_url(access=None, etag=None, unshared_at=None, allow_preview=None, password=None, vanity_name=None)`][get_shared_link_download_url]. This method returns a `unicode` string containing the shared link URL. + ```python file_id = '11111' @@ -722,30 +728,29 @@ url = client.file(file_id).get_shared_link_download_url(access='collaborators', print(f'The file shared link download URL is: {url}') ``` -[get_shared_link_download_url]: -https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_shared_link_download_url +[get_shared_link_download_url]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_shared_link_download_url -Find a File for a Shared Link ------------------------------ +## Find a File for a Shared Link To find a file given a shared link, use the [`client.get_shared_item`](https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html?highlight=get_shared_item#boxsdk.client.client.Client.get_shared_item) method. + ```python file = client.get_shared_item('https://app.box.com/s/gjasdasjhasd', password='letmein') ``` -Create or update a Shared Link --------------------- +## Create or update a Shared Link A shared link for a file can be generated or updated by calling [`file.get_shared_link(*, access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None, -allow_edit=None, password=None, vanity_name=None, **kwargs)`][get_shared_link]. +allow_edit=None, password=None, vanity_name=None, **kwargs)`][get_shared_link]. This method returns a `unicode` string containing the shared link URL. + ```python file_id = '11111' @@ -755,40 +760,37 @@ print(f'The file shared link URL is: {url}') [get_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_shared_link -Get a Shared Link --------------------- +## Get a Shared Link -To check for an existing shared link on a file, simply call +To check for an existing shared link on a file, simply call `file.shared_link` This method returns a `unicode` string containing the shared link URL. + ```python file_id = '11111' shared_link = client.file(file_id).get().shared_link url = shared_link['url'] ``` -Remove a Shared Link --------------------- +## Remove a Shared Link A shared link for a file can be removed by calling [`file.remove_shared_link(*, etag=None, **kwargs)`][remove_shared_link]. + ```python file_id = '11111' client.file(file_id).remove_shared_link() ``` -[remove_shared_link]: -https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link - +[remove_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link -Get an Embed Link ------------------ +## Get an Embed Link -A file embed URL can be generated by calling [`file.get_embed_url()`][get_embed_url]. This method returns a `unicode` +A file embed URL can be generated by calling [`file.get_embed_url()`][get_embed_url]. This method returns a `unicode` string containing a URL suitable for embedding in an `') [get_embed_url]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_embed_url -Get File Representations ------------------------- +## Get File Representations To get the preview representations of a file, call the [`file.get_representation_info(rep_hints=None)`][get_representation_info] method with the [representation hints][rep_hints] to fetch — if no hints are provided, limited information about all available -representations will be returned. This method returns a `list` of `dict`s containing the information about the +representations will be returned. This method returns a `list` of `dict`s containing the information about the requested [representations][rep_api_obj]. Note that this method only provides information about a set of available representations; your @@ -828,38 +829,37 @@ for rep in representations: [rep_hints]: https://developer.box.com/en/reference/get-files-id/#param-X-Rep-Hints [rep_api_obj]: https://developer.box.com/en/reference/resources/representations -Get Thumbnail (Deprecated) --------------------------- +## Get Thumbnail (Deprecated) A thumbnail for a file can be retrieved by calling [`file.get_thumbnail(extension='png', min_width=None, min_height=None, max_width=None, max_height=None)`][get_thumbnail]. This method returns the `bytes` of the thumbnail image. + ```python file_id = '11111' -thumbnail = client.file(file_id).get_thumbnail(extension='jpg') +thumbnail = client.file(file_id).get_thumbnail(extension='jpg') ``` [get_thumbnail]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_thumbnail -Get Thumbnail -------------- +## Get Thumbnail -A thumbnail for a file can now be retrieved by calling [`file.get_thumbnail_representation(dimensions, extension='png')`][get_thumbnail_representation]. This method returns the `bytes` of the thumbnail image. You must pass in a dimension that is valid for the extension you pass in for this file. To find valid dimensions, you must first make a call with [`file.get_representation_info(rep_hints=None)`]. This will return a `dict` of all available representations with their extensions and dimensions. More details about can be found on our developer docs [here](https://developer.box.com/guides/representations/list-all-representations/). +A thumbnail for a file can now be retrieved by calling [`file.get_thumbnail_representation(dimensions, extension='png')`][get_thumbnail_representation]. This method returns the `bytes` of the thumbnail image. You must pass in a dimension that is valid for the extension you pass in for this file. To find valid dimensions, you must first make a call with [`file.get_representation_info(rep_hints=None)`]. This will return a `dict` of all available representations with their extensions and dimensions. More details about can be found on our developer docs [here](https://developer.box.com/guides/representations/list-all-representations/). + ```python file_id = '11111' -thumbnail = client.file(file_id).get_thumbnail_representation('92x92', extension='jpg') +thumbnail = client.file(file_id).get_thumbnail_representation('92x92', extension='jpg') ``` [get_thumbnail_representation]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_thumbnail_representation -Set Metadata ------------- +## Set Metadata To set metadata on a file call the [`file.metadata(scope='global', template='properties')`][metadata] to specify the scope and template key of the metadata template to attach. Then, call the [`metadata.set(data)`][metadata_set] method with the key/value pairs to attach. This method returns a `dict` containing the applied metadata instance. @@ -875,17 +875,18 @@ applied_metadata = client.file(file_id='11111').metadata(scope='enterprise', tem print(f'Set metadata in instance ID {applied_metadata["$id"]}') ``` -Metadata can be added to a file either as free-form key/value pairs or from an existing template. To add metadata to +Metadata can be added to a file either as free-form key/value pairs or from an existing template. To add metadata to a file, first call [`file.metadata(scope='global', template='properties')`][metadata] to specify the scope and -template key of the metadata template to attach (or use the default values to attach free-form keys and values). Then, -call [`metadata.create(data)`][metadata_create] with the key/value pairs to attach. This method can only be used to +template key of the metadata template to attach (or use the default values to attach free-form keys and values). Then, +call [`metadata.create(data)`][metadata_create] with the key/value pairs to attach. This method can only be used to attach a given metadata template to the file for the first time, and returns a `dict` containing the applied metadata instance. -Note: This method will only succeed if the provided metadata template is not currently applied to the file, otherwise it will +Note: This method will only succeed if the provided metadata template is not currently applied to the file, otherwise it will fail with a Conflict error. + ```python metadata = { 'foo': 'bar', @@ -897,11 +898,11 @@ print(f'Applied metadata in instance ID {applied_metadata["$id"]}') ``` Updating metadata values is performed via a series of discrete operations, which are applied atomically against the -existing file metadata. First, specify which metadata will be updated by calling -[`file.metadata(scope='global', template='properties')`][metadata]. Then, start an update sequence by calling +existing file metadata. First, specify which metadata will be updated by calling +[`file.metadata(scope='global', template='properties')`][metadata]. Then, start an update sequence by calling [`metadata.start_update()`][metadata_start_update] and add update operations to the returned -[`MetadataUpdate`][metadata_update_obj]. Finally, perform the update by calling -[`metadata.update(metadata_update)`][metadata_update]. This final method returns a `dict` of the updated metadata +[`MetadataUpdate`][metadata_update_obj]. Finally, perform the update by calling +[`metadata.update(metadata_update)`][metadata_update]. This final method returns a `dict` of the updated metadata instance. Note: This method will only succeed if the provided metadata template has already been applied to the file; If the file does not @@ -909,6 +910,7 @@ have existing metadata, this method will fail with a Not Found error. This is us since it will save an API call compared to `set()`. + ```python file_obj = client.file(file_id='11111') file_metadata = file_obj.metadata(scope='enterprise', template='myMetadata') @@ -929,15 +931,15 @@ print(f'foo is now {updated_metadata["foo"]} and baz is now {updated_metadata["b [metadata_update_obj]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.MetadataUpdate [metadata_update]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.update -Get Metadata ------------- +## Get Metadata To retrieve the metadata instance on a file for a specific metadata template, first call [`file.metadata(scope='global', template='properties')`][metadata] to specify the scope and template key of the metadata template to retrieve, then call [`metadata.get()`][metadata_get] to retrieve the metadata values attached to -the file. This method returns a `dict` containing the applied metadata instance. +the file. This method returns a `dict` containing the applied metadata instance. + ```python metadata = client.file(file_id='11111').metadata(scope='enterprise', template='myMetadata').get() print(f'Got metadata instance {metadata["$id"]}') @@ -945,8 +947,7 @@ print(f'Got metadata instance {metadata["$id"]}') [metadata_get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.get -Remove Metadata ---------------- +## Remove Metadata To remove a metadata instance from a file, call [`file.metadata(scope='global', template='properties')`][metadata] to specify the scope and template key of the @@ -954,21 +955,22 @@ metadata template to remove, then call [`metadata.delete()`][metadata_delete] to This method returns `True` to indicate that the removal succeeded. + ```python client.file(file_id='11111').metadata(scope='enterprise', template='myMetadata').delete() ``` [metadata_delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.delete -Get All Metadata ----------------- +## Get All Metadata -To retrieve all metadata attached to a file, call [`file.get_all_metadata()`][get_all_metadata]. This method returns a -[`BoxObjectCollection`][box_object_collection] that can be used to iterate over the `dict`s representing each metadata +To retrieve all metadata attached to a file, call [`file.get_all_metadata()`][get_all_metadata]. This method returns a +[`BoxObjectCollection`][box_object_collection] that can be used to iterate over the `dict`s representing each metadata instance attached to the file. + ```python file_metadata = client.file(file_id='11111').get_all_metadata() for instance in file_metadata: @@ -978,13 +980,12 @@ for instance in file_metadata: [get_all_metadata]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.get_all_metadata -Set a Classification --------------------- +## Set a Classification It is important to note that this feature is only available if you have Governance. To add classification to a [`File`][file_class], call [`file.set_classification(classification)`][set_classification]. -This method returns the classification type on the [`File`][file_class] object. If a classification already exists then +This method returns the classification type on the [`File`][file_class] object. If a classification already exists then this call will update the existing classification with the new [`ClassificationType`][classification_type_class]. ```python @@ -995,10 +996,10 @@ print(f'Classification Type is: {classification}') ``` The set method will always work no matter the state your [`File`][file_class] is in. For cases already where a -classification value already exists [`set_classification(classification)`][set_classification] may make multiple -API calls. +classification value already exists [`set_classification(classification)`][set_classification] may make multiple +API calls. -Alternatively, if you already know you have a classification and you are simple updating it, you can use the +Alternatively, if you already know you have a classification and you are simple updating it, you can use the [`update_classification(classification)`][update_classification]. This will ultimately help you save one extra API call. ```python @@ -1010,8 +1011,7 @@ print(f'Classification Type is: {classification}') [update_classification]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.update_classification [classification_type_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.ClassificationType -Retrieve a Classification -------------------------- +## Retrieve a Classification To retrieve a classification from a [`File`][file_class], call [`file.get_classification()`][get_classification]. This method returns the classification type on the [`File`][file_class] object. @@ -1023,8 +1023,7 @@ print(f'Classification Type is: {classification}') [get_classification]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.get_classification -Remove a Classification ------------------------ +## Remove a Classification To remove a classification from a [`File`][file_class], call [`file.remove_classification()`][remove_classification]. @@ -1034,14 +1033,13 @@ client.file(file_id='11111').remove_classification() [remove_classification]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_classification -Set retention policy expiration date -------------------------------------- +## Set retention policy expiration date To set new retention policy expiration date for the file, call [`set_disposition_at(date_time)`][set_disposition_at]. This method will only work for files under retention with `permanently_delete` disposition action set. Remember that disposition date can't be shortened once set on a file. As the `date_time` parameter you can use either datetime string, e.g. '2035-03-04T10:14:24+14:00' or -`datetime.datetime` object. +`datetime.datetime` object. ```python import datetime, pytz diff --git a/docs/usage/folders.md b/docs/usage/folders.md index 9c02d0c4e..f9d1c1a88 100644 --- a/docs/usage/folders.md +++ b/docs/usage/folders.md @@ -1,5 +1,4 @@ -Folders -======= +# Folders Folder objects represent a folder from a user's account. They can be used to iterate through a folder's contents, collaborate a folder with another user or @@ -35,18 +34,18 @@ group, and perform other common folder operations (move, copy, delete, etc.). -Get Information About a Folder ------------------------------- +## Get Information About a Folder To retrieve information about a folder, first call [`client.folder(folder_id)`][folder] to initialize the -[`Folder`][folder_class] object. Then, call [`folder.get(*, fields=None, etag=None, **kwargs)`][get] to retrieve data about the -folder. This method returns a new [`Folder][folder_class] object with fields populated by data from the API, leaving +[`Folder`][folder_class] object. Then, call [`folder.get(*, fields=None, etag=None, **kwargs)`][get] to retrieve data about the +folder. This method returns a new [`Folder][folder_class] object with fields populated by data from the API, leaving the original object unmodified. You can pass a list of `fields` to retrieve from the API in order to filter to just the necessary fields or add ones not returned by default. + ```python folder = client.folder(folder_id='22222').get() print(f'Folder "{folder.name}" has {folder.item_collection["total_count"]} items in it') @@ -56,8 +55,7 @@ print(f'Folder "{folder.name}" has {folder.item_collection["total_count"]} items [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.get [folder_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder -Get the User's Root Folder --------------------------- +## Get the User's Root Folder To get the current user's root folder, call [`client.root_folder()`][root_folder] to initialize the appropriate [`Folder`][folder_class] object. @@ -68,8 +66,7 @@ root_folder = client.root_folder().get() [root_folder]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.root_folder -Get the Items in a Folder -------------------------- +## Get the Items in a Folder To retrieve the items in a folder, call [`folder.get_items(limit=None, offset=0, marker=None, use_marker=False, sort=None, direction=None, fields=None)`][get_items]. @@ -77,6 +74,7 @@ This method returns a `BoxObjectCollection` that allows you to iterate over all the collection. + ```python items = client.folder(folder_id='22222').get_items() for item in items: @@ -86,14 +84,14 @@ for item in items: [get_items]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.get_items [item_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item -Update a Folder ---------------- +## Update a Folder To update a folder's information, call [`folder.update_info(*, data, etag=None, **kwargs)`][update_info] with a `dict` -of properties to update on the folder. This method returns a new updated [`Folder`][folder_class] object, leaving +of properties to update on the folder. This method returns a new updated [`Folder`][folder_class] object, leaving the original object unmodified. + ```python updated_folder = client.folder(folder_id='22222').update_info(data={ 'name': '[ARCHIVED] Planning documents', @@ -104,14 +102,14 @@ print('Folder updated!') [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.update_info -Create a Folder ---------------- +## Create a Folder A folder can be created by calling [`folder.create_subfolder(name)`][create_subfolder] on the parent folder with the -name of the subfolder to be created. This method returns a new [`Folder`][folder_class] representing the created +name of the subfolder to be created. This method returns a new [`Folder`][folder_class] representing the created subfolder. + ```python subfolder = client.folder('0').create_subfolder('My Stuff') print(f'Created subfolder with ID {subfolder.id}') @@ -119,14 +117,14 @@ print(f'Created subfolder with ID {subfolder.id}') [create_subfolder]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.create_subfolder -Copy a Folder -------------- +## Copy a Folder A folder can be copied into a new parent folder by calling [`folder.copy(parent_folder, name=None)`][copy] with the destination folder and an optional new name for the file in case there is a name conflict in the destination folder. This method returns a new [`Folder`][folder_class] object representing the copy of the folder in the destination folder. + ```python folder_id = '22222' destination_folder_id = '44444' @@ -140,12 +138,11 @@ print(f'Folder "{folder_copy.name}" has been copied into folder "{folder_copy.pa [copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.copy -Move a Folder -------------- +## Move a Folder To move a folder from one parent folder into another, call [`folder.move(parent_folder, name=None)`][move] with the -destination folder to move the folder into. You can optionally provide a `name` parameter to automatically rename the -folder in case of a name conflict in the destination folder. This method returns the updated [`Folder`][folder_class] +destination folder to move the folder into. You can optionally provide a `name` parameter to automatically rename the +folder in case of a name conflict in the destination folder. This method returns the updated [`Folder`][folder_class] object in the new folder. ```python @@ -161,8 +158,7 @@ print(f'Folder "{moved_folder.name}" has been moved into folder "{moved_folder.p [move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.move -Rename a File ------------ +## Rename a File A folder can be renamed by calling [`folder.rename(name)`][rename]. This method returns the updated [`Folder`][folder_class] object with a new name. @@ -176,38 +172,36 @@ print(f'Folder was renamed to "{renamed_folder.name}"') [rename]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.rename -Delete a Folder ---------------- +## Delete a Folder -Calling the [`folder.delete(recursive=True, etag=None)`][delete] method will delete the folder. Depending on enterprise -settings, this will either move the folder to the user's trash or permanently delete the folder. This method returns +Calling the [`folder.delete(recursive=True, etag=None)`][delete] method will delete the folder. Depending on enterprise +settings, this will either move the folder to the user's trash or permanently delete the folder. This method returns `True` to signify that the deletion was successful. By default, the method will delete the folder and all of its contents; to fail the deletion if the folder is not empty, set the `recursive` parameter to `False`. + ```python client.folder(folder_id='22222').delete() ``` -[delete]: -https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.delete +[delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.delete -Find a Folder for a Shared Link ------------------------------ +## Find a Folder for a Shared Link To find a folder given a shared link, use the [`client.get_shared_item`](https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html?highlight=get_shared_item#boxsdk.client.client.Client.get_shared_item) method. + ```python folder = client.get_shared_item('https://app.box.com/s/gjasdasjhasd', password='letmein') ``` -Create or update a Shared Link --------------------- +## Create or update a Shared Link A shared link for a folder can be generated or updated by calling [`folder.get_shared_link(access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None, @@ -215,6 +209,7 @@ password=None, vanity_name=None, **kwargs)`][get_shared_link]. This method returns a `unicode` string containing the shared link URL. + ```python folder_id = '11111' @@ -224,40 +219,36 @@ print(f'The folder shared link URL is: {url}') [get_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.get_shared_link +## Get a Shared Link -Get a Shared Link --------------------- - -To check for an existing shared link on a folder, simply call +To check for an existing shared link on a folder, simply call `folder.shared_link` This method returns a `unicode` string containing the shared link URL. + ```python folder_id = '11111' shared_link = client.folder(folder_id).get().shared_link url = shared_link['url'] ``` -Remove a Shared Link --------------------- +## Remove a Shared Link A shared link for a folder can be removed by calling [`folder.remove_shared_link(etag=None, **kwargs)`][remove_shared_link]. + ```python folder_id = '11111' client.folder(folder_id).remove_shared_link() ``` -[remove_shared_link]: -https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link - +[remove_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link -Set Metadata ------------- +## Set Metadata To set metadata on a folder call the [`folder.metadata(scope='global', template='properties')`][metadata] to specify the scope and template key of the metadata template to attach. Then, call the [`metadata.set(data)`][metadata_set] method with the key/value pairs to attach. This method returns a `dict` containing the applied metadata instance. @@ -273,17 +264,18 @@ applied_metadata = client.folder(folder_id='11111').metadata(scope='enterprise', print(f'Set metadata in instance ID {applied_metadata["$id"]}') ``` -Metadata can be added to a folder either as free-form key/value pairs or from an existing template. To add metadata to +Metadata can be added to a folder either as free-form key/value pairs or from an existing template. To add metadata to a folder, first call [`folder.metadata(scope='global', template='properties')`][metadata] to specify the scope and template key of the metadata template to attach (or use the default values to attach free-form keys and values). -Then, call [`metadata.create(data)`][metadata_create] with the key/value pairs to attach. This method can only be used +Then, call [`metadata.create(data)`][metadata_create] with the key/value pairs to attach. This method can only be used to attach a given metadata template to the folder for the first time, and returns a `dict` containing the applied metadata instance. -Note: This method will only succeed if the provided metadata template is not currently applied to the folder, otherwise it will +Note: This method will only succeed if the provided metadata template is not currently applied to the folder, otherwise it will fail with a Conflict error. + ```python metadata = { 'foo': 'bar', @@ -295,11 +287,11 @@ print(f'Applied metadata in instance ID {applied_metadata["$id"]}') ``` Updating metadata values is performed via a series of discrete operations, which are applied atomically against the -existing folder metadata. First, specify which metadata will be updated by calling -[`folder.metadata(scope='global', template='properties')`][metadata]. Then, start an update sequence by calling +existing folder metadata. First, specify which metadata will be updated by calling +[`folder.metadata(scope='global', template='properties')`][metadata]. Then, start an update sequence by calling [`metadata.start_update()`][metadata_start_update] and add update operations to the returned -[`MetadataUpdate`][metadata_update_obj]. Finally, perform the update by calling -[`metadata.update(metadata_update)`][metadata_update]. This final method returns a `dict` of the updated metadata +[`MetadataUpdate`][metadata_update_obj]. Finally, perform the update by calling +[`metadata.update(metadata_update)`][metadata_update]. This final method returns a `dict` of the updated metadata instance. Note: This method will only succeed if the provided metadata template has already been applied to the folder; If the folder does not @@ -307,6 +299,7 @@ have existing metadata, this method will fail with a Not Found error. This is us since it will save an API call compared to `set()`. + ```python folder = client.folder(folder_id='22222') folder_metadata = folder.metadata(scope='enterprise', template='myMetadata') @@ -327,15 +320,15 @@ print(f'foo is now {updated_metadata["foo"]} and baz is now {updated_metadata["b [metadata_update_obj]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.MetadataUpdate [metadata_update]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.update -Get Metadata ------------- +## Get Metadata To retrieve the metadata instance on a folder for a specific metadata template, first call [`folder.metadata(scope='global', template='properties')`][metadata] to specify the scope and template key of the metadata template to retrieve, then call [`metadata.get()`][metadata_get] to retrieve the metadata values attached -to the folder. This method returns a `dict` containing the applied metadata instance. +to the folder. This method returns a `dict` containing the applied metadata instance. + ```python metadata = client.folder(folder_id='22222').metadata(scope='enterprise', template='myMetadata').get() print(f'Got metadata instance {metadata["$id"]}') @@ -343,8 +336,7 @@ print(f'Got metadata instance {metadata["$id"]}') [metadata_get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.get -Remove Metadata ---------------- +## Remove Metadata To remove a metadata instance from a folder, call [`folder.metadata(scope='global', template='properties')`][metadata] to specify the scope and template key of the @@ -352,20 +344,21 @@ metadata template to remove, then call [`metadata.delete()`][metadata_delete] to This method returns `True` to indicate that the removal succeeded. + ```python client.folder(folder_id='11111').metadata(scope='enterprise', template='myMetadata').delete() ``` [metadata_delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata.Metadata.delete -Get All Metadata ----------------- +## Get All Metadata -To retrieve all metadata attached to a folder, call [`folder.get_all_metadata()`][get_all_metadata]. This method +To retrieve all metadata attached to a folder, call [`folder.get_all_metadata()`][get_all_metadata]. This method returns a [`BoxObjectCollection`][box_object_collection] that can be used to iterate over the `dict`s representing each metadata instance attached to the folder. + ```python folder_metadata = client.folder(folder_id='22222').get_all_metadata() for instance in folder_metadata: @@ -375,11 +368,10 @@ for instance in folder_metadata: [get_all_metadata]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.get_all_metadata -Get Metadata For Folder Items ------------------------------ +## Get Metadata For Folder Items When fetching folder items, you may wish to retrieve metadata for the items simultaneously to avoid needing to make -an API call for each item. You can retrieve up to 3 metadata instances per item by passing the special +an API call for each item. You can retrieve up to 3 metadata instances per item by passing the special `metadata..` field to [`folder.get_items(limit=None, offset=0, marker=None, use_marker=False, sort=None, direction=None, fields=None)`][get_items]. The metadata is available as a multi-level `dict` on the returned [`Item`][item_class] objects. @@ -398,13 +390,12 @@ for item in items: print(f'{item.type.capitalize()} {item.id} is a vendor contract with vendor name {metadata["vendorName"]}') ``` -Set a Classification --------------------- +## Set a Classification It is important to note that this feature is available only if you have Governance. To add classification to a [`Folder`][folder_class], call [`folder.set_classification(classification)`][set_classification]. -This method returns the classification type on the [`Folder`][folder_class] object. If a classification already exists then +This method returns the classification type on the [`Folder`][folder_class] object. If a classification already exists then this call will update the existing classification with the new [`ClassificationType`][classification_type_class]. ```python @@ -415,10 +406,10 @@ print(f'Classification Type is: {classification}') ``` The set method will always work no matter the state your [`Folder`][folder_class] is in. For cases already where a -classification value already exists [`set_classification(classification)`][set_classification] may make multiple -API calls. +classification value already exists [`set_classification(classification)`][set_classification] may make multiple +API calls. -Alternatively, if you already know you have a classification and you are simple updating it, you can use the +Alternatively, if you already know you have a classification and you are simple updating it, you can use the [`update_classification(classification)`][update_classification]. This will ultimately help you save one extra API call. ```python @@ -430,8 +421,7 @@ print(f'Classification Type is: {classification}') [update_classification]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.update_classification [classification_type_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.ClassificationType -Retrieve a Classification -------------------------- +## Retrieve a Classification To retrieve a classification from a [`Folder`][folder_class], call [`folder.get_classification()`][get_classification]. This method returns the classification type on the [`Folder`][folder_class] object. @@ -443,8 +433,7 @@ print(f'Classification Type is: {classification}') [get_classification]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.get_classification -Remove a Classification ------------------------ +## Remove a Classification To remove a classification from a [`Folder`][folder_class], call [`folder.remove_classification()`][remove_classification]. @@ -454,8 +443,7 @@ client.folder(folder_id='11111').remove_classification() [remove_classification]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_classification -Create a Folder Lock -------------- +## Create a Folder Lock To lock a folder, call [`client.folder(folder_id).create_lock()`][create-folder-lock] @@ -468,9 +456,7 @@ print(f'Created a lock with ID {lock.folder.id}') [create_folder_lock]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.create_lock - -Get Folder Locks -------------------------- +## Get Folder Locks To retrieve a list of the locks on a folder, call [`client.folder(folder_id).get_locks()][get-folder-locks] @@ -484,8 +470,7 @@ print(f'A lock on a folder with ID {lock.folder.id}') [get_folder_locks]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.get_locks -Delete a Folder Lock ------------------- +## Delete a Folder Lock To remove a folder lock, call [`client.folder_lock(folder_lock_id).delete()`][delete-folder-lock] diff --git a/docs/usage/group.md b/docs/usage/group.md index ae0f7a54f..22199148d 100644 --- a/docs/usage/group.md +++ b/docs/usage/group.md @@ -1,11 +1,10 @@ -Groups -====== +# Groups + Groups are sets of users that can be used in collaborations. - - [List Groups](#list-groups) - [Create a Group](#create-a-group) - [Get Information about a Group](#get-information-about-a-group) @@ -21,14 +20,14 @@ Groups are sets of users that can be used in collaborations. -List Groups ------------ +## List Groups Calling [`client.get_groups(name=None, limit=None, offset=None, fields=None)`][get_groups] will return a `BoxObjectCollection` that allows you to iterate over the [`Group`][group_class] objects representing groups in the enterprise. + ```python groups = client.get_groups() for group in groups: @@ -47,17 +46,17 @@ for group in groups: [get_groups]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_groups [group_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.group.Group -Create a Group --------------- +## Create a Group To create a new group, call -[`client.create_group(name, provenance=None, external_sync_identifier=None, description=None, invitability_level=None, member_viewability_level=None, fields=None)`][create_group] with the name of the group and any optional group properties you want to set. This method +[`client.create_group(name, provenance=None, external_sync_identifier=None, description=None, invitability_level=None, member_viewability_level=None, fields=None)`][create_group] with the name of the group and any optional group properties you want to set. This method returns a [`Group`][group_class] object representing the created group. You can read more about the optional parameters in the [Create Group API documentation](https://developer.box.com/en/reference/post-groups/). + ```python created_group = client.create_group('Example Group') print(f'Created group with ID {created_group.id}') @@ -65,15 +64,15 @@ print(f'Created group with ID {created_group.id}') [create_group]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.create_group -Get Information about a Group ------------------------------ +## Get Information about a Group To retrieve information about a group, first call [`client.group(group_id)`][group] to initialize a -[`Group`][group_class] object. Then, call [`group.get(*, fields=None, headers=None, **kwargs)`][get] to retrieve the +[`Group`][group_class] object. Then, call [`group.get(*, fields=None, headers=None, **kwargs)`][get] to retrieve the data about that group. This method returns a new [`Group`][group_class] object with fields populated by data form the API, leaving the original object unmodified. + ```python group = client.group(group_id='11111').get() print(f'Got group {group.name}') @@ -90,15 +89,14 @@ print(f'The "{group.name}" group ({group.description}) came from {group.provenan [group]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.group [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get - -Update a Group --------------- +## Update a Group To update a group, call [`group.update_info(data=group_update)`][update_info] with a `dict` of the group properties -to update. This method returns a new [`Group`][group_class] object with the updates applied, leaving the original +to update. This method returns a new [`Group`][group_class] object with the updates applied, leaving the original object unmodified. + ```python group_update = {'name': 'New Group Name'} updated_group = client.group(group_id='11111').update_info(data=group_update) @@ -107,13 +105,13 @@ print(f'Changed the name of group {updated_group.id} to "{updated_group.name}"') [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Delete a Group --------------- +## Delete a Group -To delete a group, call [`group.delete()`][delete]. This method returns `True` to indicate that the deletion was +To delete a group, call [`group.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python client.group(group_id='11111').delete() print('The group was deleted!') @@ -121,15 +119,15 @@ print('The group was deleted!') [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete -Get a Group's Collaborations ----------------------------- +## Get a Group's Collaborations To retrieve all collaborations for a group, call -[`group.get_collaborations(limit=None, offset=None, fields=None)`][get_collaborations]. This method returns a +[`group.get_collaborations(limit=None, offset=None, fields=None)`][get_collaborations]. This method returns a `BoxObjectCollection` that allows you to iterate over the [`Collaboration`][collaboration_class] objects in the collection. + ```python collaborations = client.group(group_id='11111').get_collaborations() for collaboration in collaborations: @@ -139,18 +137,18 @@ for collaboration in collaborations: [get_collaborations]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.group.Group.get_collaborations [collaboration_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collaboration.Collaboration -Add User to Group ------------------ +## Add User to Group To add a new member to the group, call [`group.add_member(user, role='member', configurable_permissions=None)`][add_member] with the [`User`][user_class] to -add to the group. This method returns a new [`GroupMembership`][membership_class] object representing the presence of +add to the group. This method returns a new [`GroupMembership`][membership_class] object representing the presence of the user in the group. You can optionally specify the user's `role` in the group, and for users with an admin role you can configure which permissions they have in the group by passing a `dict` of [group permissions][permissions] to `configurable_permissions`. + ```python user = client.user('1111') membership = client.group(group_id='11111').add_member(user) @@ -161,16 +159,16 @@ print(f'Added {membership.user.name} to the {membership.group.name} group!') [user_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User [permissions]: https://developer.box.com/en/reference/resources/group-membership/ -Get Information about a Group Membership ----------------------------------------- +## Get Information about a Group Membership To retrieve information about a group membership, first call [`client.group_membership(group_membership_id)`][group_membership] to initialize the -[`GroupMembership`][membership_class] object. Then, call [`group_membership.get(*, fields=None, headers=None, **kwargs)`][get] -to retrieve data about the group membership from the API. This returns a new [`GroupMembership`][membership_class] +[`GroupMembership`][membership_class] object. Then, call [`group_membership.get(*, fields=None, headers=None, **kwargs)`][get] +to retrieve data about the group membership from the API. This returns a new [`GroupMembership`][membership_class] object with fields populated by data from the API, leaving the original object unmodified. + ```python membership_id = '11111' membership = client.group_membership(membership_id).get() @@ -180,14 +178,14 @@ print(f'User "{membership.user.name}" is a member of the {membership.group.name} [group_membership]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.group_membership [membership_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.group_membership.GroupMembership -Update Group Membership ------------------------ +## Update Group Membership To update a group membership, call [`membership.update_info(data=membership_update)`][update_info] with a `dict` of -properties to update on the membership object. This method returns a new [`GroupMembership`][membership_class] object +properties to update on the membership object. This method returns a new [`GroupMembership`][membership_class] object with the changes applied, leaving the original object unmodified. + ```python membership_id = '1234' membership_update = {'role': 'admin'} @@ -195,28 +193,28 @@ updated_membership = client.group_membership(membership_id).update_info(data=mem print(f'Updated {updated_membership.user.name}\'s group role to {updated_membership.role}') ``` -Remove User from Group ----------------------- +## Remove User from Group To remove a user from a group, delete their associated group membership by calling [`group_membership.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python membership_id = '1234' -client.group_membership(membership_id).delete() +client.group_membership(membership_id).delete() print('The membership was deleted!') ``` -List Group Members ------------------- +## List Group Members To retrieve all of the memberships for a given group, call -[`group.get_memberships(limit=None, offset=0, fields=None)`][get_memberships]. This method returns a +[`group.get_memberships(limit=None, offset=0, fields=None)`][get_memberships]. This method returns a `BoxObjectCollection` that allows you to iterate over all of the [`GroupMembership`][membership_class] objects in the collection. + ```python group_memberships = client.group(group_id='11111').get_memberships() for membership in group_memberships: @@ -225,15 +223,15 @@ for membership in group_memberships: [get_memberships]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.group.Group.get_memberships -List Memberships for User -------------------------- +## List Memberships for User To retrieve all of the groups a user belongs to, get a list of their associated group memberships by calling -[`user.get_group_memberships(limit=None, offset=0, fields=None)`][get_group_memberships]. This method returns a +[`user.get_group_memberships(limit=None, offset=0, fields=None)`][get_group_memberships]. This method returns a `BoxObjectCollection` that allows you to iterate over the [`GroupMembership`][membership_class] objects in the collection. + ```python user_memberships = client.user(user_id='33333').get_group_memberships() for membership in user_memberships: diff --git a/docs/usage/legal_hold.md b/docs/usage/legal_hold.md index 96a452d85..62eda28a1 100644 --- a/docs/usage/legal_hold.md +++ b/docs/usage/legal_hold.md @@ -1,6 +1,4 @@ -Legal Hold Policies -=================== - +# Legal Hold Policies A legal hold policy blocks permanent deletion of content during ongoing litigation. Admins can create legal hold policies and then later assign them to specific folders, files, or users. @@ -14,7 +12,6 @@ created to keep track of everything that needs to be held. The actual holding is - - [Get Information About a Legal Hold Policy](#get-information-about-a-legal-hold-policy) - [List Legal Hold Policies](#list-legal-hold-policies) - [Create New Legal Hold Policy](#create-new-legal-hold-policy) @@ -29,15 +26,15 @@ created to keep track of everything that needs to be held. The actual holding is -Get Information About a Legal Hold Policy ------------------------------------------ +## Get Information About a Legal Hold Policy To retrieve information about a legal hold policy, first call [`client.legal_hold_policy(policy_id)`][legal_hold_policy] to initialize the [`LegalHoldPolicy`][policy_class] and then call [`legal_hold_policy.get(*, fields=None, headers=None, **kwargs)`][get] to -retrieve data from the API. This method returns a new [`LegalHoldPolicy`][policy_class] object with fields populated by +retrieve data from the API. This method returns a new [`LegalHoldPolicy`][policy_class] object with fields populated by data form the API, leaving the original object unmodified. + ```python legal_hold_policy = client.legal_hold_policy(policy_id='12345').get() print(f'The "{legal_hold_policy.policy_name}" policy is {legal_hold_policy.status}') @@ -47,16 +44,16 @@ print(f'The "{legal_hold_policy.policy_name}" policy is {legal_hold_policy.statu [policy_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.legal_hold_policy.LegalHoldPolicy [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -List Legal Hold Policies ------------------------- +## List Legal Hold Policies To get the legal hold policies available in the enterprise, call [`client.get_legal_hold_policies(policy_name=None, limit=None, marker=None, fields=None)`][get_legal_hold_policies]. You can optionally pass a `policy_name` value to filter the results to include only policies that are a case-insensitive -prefix match by name. This method returns a `BoxObjectCollection` that allows you to iterate over the +prefix match by name. This method returns a `BoxObjectCollection` that allows you to iterate over the [`LegalHoldPolicy`][policy_class] objects in the collection. + ```python policies = client.get_legal_hold_policies() for policy in policies: @@ -65,16 +62,16 @@ for policy in policies: [get_legal_hold_policies]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_legal_hold_policies -Create New Legal Hold Policy ----------------------------- +## Create New Legal Hold Policy To create a new legal hold policy, call -[`client.create_legal_hold_policy(policy_name, description=None, filter_starting_at=None, filter_ending_at=None, is_ongoing=None)`][create_legal_hold_policy] with the name for the policy. You can optionally include a human-readable `description`, as -well as parameters describing which time period the policy applies to. You must specify either `filter_starting_at` -and `filter_ending_at` dates, or `is_ongoing=True`. This method returns a new [`LegalHoldPolicy`][policy_class] object +[`client.create_legal_hold_policy(policy_name, description=None, filter_starting_at=None, filter_ending_at=None, is_ongoing=None)`][create_legal_hold_policy] with the name for the policy. You can optionally include a human-readable `description`, as +well as parameters describing which time period the policy applies to. You must specify either `filter_starting_at` +and `filter_ending_at` dates, or `is_ongoing=True`. This method returns a new [`LegalHoldPolicy`][policy_class] object representing the created policy. + ```python new_policy = client.create_legal_hold_policy('New Policy', is_ongoing=True) print(f'Created legal hold policy with ID {new_policy.id}') @@ -82,14 +79,14 @@ print(f'Created legal hold policy with ID {new_policy.id}') [create_legal_hold_policy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.create_legal_hold_policy -Update Legal Hold Policy ------------------------- +## Update Legal Hold Policy To update an existing legal hold policy, call [`legal_hold_policy.update_info(data=policy_update)`][update_info] with a `dict` of properties to update on the policy. This method returns a new [`LegalHoldPolicy`][policy_class] object with the updates applied, leaving the original object unmodified. + ```python policy_update = {'description': 'New Description', 'release_notes': 'Example Notes'} updated_policy = client.legal_hold_policy(policy_id='12345').update_info(data=policy_update) @@ -97,16 +94,16 @@ updated_policy = client.legal_hold_policy(policy_id='12345').update_info(data=po [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Delete Legal Hold Policy ------------------------- +## Delete Legal Hold Policy -To delete a legal hold policy, call [`legal_hold_policy.delete()`][delete]. This method returns `True` to indicate that +To delete a legal hold policy, call [`legal_hold_policy.delete()`][delete]. This method returns `True` to indicate that the deletion request was successful. -> __Note:__ This is an asynchronous process - the policy assignment may not be fully deleted yet when the +> **Note:** This is an asynchronous process - the policy assignment may not be fully deleted yet when the > response comes back. + ```python client.legal_hold_policy(policy_id='12345').delete() print('Legal hold policy was deleted!') @@ -114,14 +111,14 @@ print('Legal hold policy was deleted!') [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete -Assign Legal Hold Policy ------------------------- +## Assign Legal Hold Policy -To assign a legal hold policy, call [`legal_hold_policy.assign(assignee)`][assign]. You can assign a legal hold policy +To assign a legal hold policy, call [`legal_hold_policy.assign(assignee)`][assign]. You can assign a legal hold policy to a [`Folder`][folder_class], [`File`][file_class], [`FileVersion`][file_version_class], or [`User`][user_class]. This will cause the associated items to be held and unable to be deleted. + ```python folder_to_assign = client.folder(folder_id='22222') assignment = client.legal_hold_policy(policy_id'12345').assign(folder_to_assign) @@ -134,8 +131,7 @@ print(f'Applied policy "{assignment.legal_hold_policy.policy_name}" to {assignme [file_version_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file_version.FileVersion [user_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User -List Legal Hold Policy Assignments ----------------------------------- +## List Legal Hold Policy Assignments To get the assignments for a specific legal hold policy, call [`legal_hold_policy.get_assignments(assign_to_type=None, assign_to_id=None, limit=None, marker=None, fields=None)`][get_assignments]. @@ -143,6 +139,7 @@ This method returns a `BoxObjectCollection` that allows you to iterate over the [`LegalHoldPolicyAssignment`][assignment_class] objects in the collection. + ```python assignments = client.legal_hold_policy(policy_id='12345').get_assignments() for assignment in assignments: @@ -161,17 +158,17 @@ for assignment in assignments: [get_assignments]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.legal_hold_policy.LegalHoldPolicy.get_assignments [assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.legal_hold_policy_assignment.LegalHoldPolicyAssignments -Get Information About a Legal Hold Assignment ---------------------------------------------- +## Get Information About a Legal Hold Assignment To retrieve information about a legal hold policy assignment, first call [`client.legal_hold_policy_assignment(policy_assignment_id)`][legal_hold_policy_assignment] to initialize the [`LegalHoldPolicyAssignment`][assignment_class] and then call [`legal_hold_policy_assignment.get(*, fields=None, headers=None, **kwargs)`][get] to -retrieve data about the assignment from the API. This method returns a new +retrieve data about the assignment from the API. This method returns a new [`LegalHoldPolicyAssignment`][assignment_class] with fields populated by data from the API, leaving the original object unmodified. + ```python assignment_id = '98765' assignment = client.legal_hold_policy_assignment(assignment_id).get() @@ -180,30 +177,30 @@ print(f'Policy {assignment.legal_hold_policy.id} is assigned to {assignment.assi [legal_hold_policy_assignment]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.legal_hold_policy_assignment -Delete Legal Hold Assignment ----------------------------- +## Delete Legal Hold Assignment -To delete an existing legal hold policy assignment, call [`legal_hold_policy_assignment.delete()`][delete]. This method +To delete an existing legal hold policy assignment, call [`legal_hold_policy_assignment.delete()`][delete]. This method returns `True` to indicate that the deletion request was successful. -> __Note:__ This is an asynchronous process - the policy assignment may not be fully deleted yet when the +> **Note:** This is an asynchronous process - the policy assignment may not be fully deleted yet when the > response comes back. + ```python assignment_id = '1111' client.legal_hold_policy_assignment(assignment_id).delete() ``` -List File Version Legal Holds ------------------------------ +## List File Version Legal Holds To get the actual hold records associated with a policy, call -[`legal_hold_policy.get_file_version_legal_holds()`][get_file_version_legal_holds]. This method returns a +[`legal_hold_policy.get_file_version_legal_holds()`][get_file_version_legal_holds]. This method returns a `BoxObjectCollection` that allows you to iterate over the [`LegalHold`][hold_class] objects in the collection. + ```python legal_holds = client.legal_hold_policy(policy_id='12345').get_file_version_legal_holds() for legal_hold in legal_holds: @@ -213,14 +210,14 @@ for legal_hold in legal_holds: [get_file_version_legal_holds]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.legal_hold_policy.LegalHoldPolicy.get_file_version_legal_holds [hold_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.legal_hold.LegalHold -Get Information about a File Version Legal Hold ------------------------------------------------ +## Get Information about a File Version Legal Hold -To retrieve information about a file version legal hold, call [`legal_hold.get(*, fields=None, headers=None, **kwargs)`][get]. This method +To retrieve information about a file version legal hold, call [`legal_hold.get(*, fields=None, headers=None, **kwargs)`][get]. This method returns a new [`LegalHold`][hold_class] with fields populated by data from the API, leaving the original object unmodified. + ```python file_version_legal_hold_id = '55555' legal_hold = client.legal_hold(file_version_legal_hold_id).get() diff --git a/docs/usage/metadata.md b/docs/usage/metadata.md index 94150a306..3dfd3615a 100644 --- a/docs/usage/metadata.md +++ b/docs/usage/metadata.md @@ -1,5 +1,4 @@ -Metadata -======== +# Metadata Metadata allows users and applications to define and store custom data associated with their files/folders. Metadata consists of key:value pairs that belong to @@ -24,7 +23,6 @@ in a flexible way, without pre-defined template structure. - - [Create Metadata Template](#create-metadata-template) - [Get Metadata Template](#get-metadata-template) - [Get by scope and template key](#get-by-scope-and-template-key) @@ -35,17 +33,17 @@ in a flexible way, without pre-defined template structure. -Create Metadata Template ------------------------- +## Create Metadata Template To create a new metadata template, call [`client.create_metadata_template(display_name, fields, template_key=None, hidden=False, scope='enterprise', copy_instance_on_item_copy=False)`][create_template] with the human-readable name of the template and the [`MetadataField`s][metadata_field_class] the template should have. -You can optionally specify a key for the template, otherwise one will be derived from the display name. At the current -time, only `enterprise` scope templates are supported. This method returns a +You can optionally specify a key for the template, otherwise one will be derived from the display name. At the current +time, only `enterprise` scope templates are supported. This method returns a [`MetadataTemplate`][metadata_template_class] object representing the created template. + ```python from boxsdk.object.metadata_template import MetadataField, MetadataFieldType @@ -62,18 +60,18 @@ print(f'Metadata template ID {template.scope}/{template.templateKey} created!') [metadata_field_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_template.MetadataField [metadata_template_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_template.MetadataTemplate -Get Metadata Template ---------------------- +## Get Metadata Template ### Get by scope and template key To retrieve a specific template by scope and template key, first use [`client.metadata_template(scope, template_key)`][metadata_template] to construct the appropriate [`MetadataTemplate`][metadata_template_class] object, and then call [`template.get()`][get] to retrieve data about -the template. This method returns a new [`MetadataTemplate`][metadata_template_class] object with fields populated by +the template. This method returns a new [`MetadataTemplate`][metadata_template_class] object with fields populated by data from the API, leaving the original object unmodified. + ```python template = client.metadata_template('enterprise', 'employeeRecord').get() print(f'The {template.displayName} template has {len(template.fields)} fields') @@ -85,10 +83,11 @@ print(f'The {template.displayName} template has {len(template.fields)} fields') ### Get by template ID To retrieve a template by ID, call [`client.get_metadata_template_by_id(template_id)`][get_by_id] with the ID of the -metadata template. This method returns a [`MetadataTemplate`][metadata_template_class] object with fields populated by +metadata template. This method returns a [`MetadataTemplate`][metadata_template_class] object with fields populated by data from the API. + ```python template = client.metadata_template_by_id(template_id='abcdef-fba434-ace44').get() print(f'The {template.displayName} template has {len(template.fields)} fields') @@ -96,16 +95,16 @@ print(f'The {template.displayName} template has {len(template.fields)} fields') [get_by_id]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_metadata_template_by_id -Update Metadata Template ------------------------- +## Update Metadata Template To make changes to a metadata template, first call [`template.start_update()`][start_update] to create a -[`MetadataTemplateUpdate`][template_update_class] to track updates. Call the methods on this object to add the +[`MetadataTemplateUpdate`][template_update_class] to track updates. Call the methods on this object to add the necessary update operations, and then call [`template.update_info(*, updates, **kwargs)`][update_info] with the updates -object to apply the changes to the metadata template. This method returns an updated +object to apply the changes to the metadata template. This method returns an updated [`MetadataTemplate`][metadata_template_class] object with the changes applied, leaving the original object unmodified. + ```python template = client.metadata_template('enterprise', 'employeeRecord') updates = template.start_update() @@ -119,16 +118,16 @@ updated_template = template.update_info(updates=updates) [template_update_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_template.MetadataTemplateUpdate [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_template.MetadataTemplate.update_info -Get Enterprise Metadata Templates ---------------------------------- +## Get Enterprise Metadata Templates Get all metadata templates for the current enterprise by calling [`client.get_metadata_templates(scope='enterprise', limit=None, marker=None, fields=None)`][get_metadata_templates]. By default, this retrieves all templates scoped to the current enterprise, but you can pass the `scope` parameter to -retrieve templates for a different scope. This method returns a [`BoxObjectCollection`][box_object_collection] that +retrieve templates for a different scope. This method returns a [`BoxObjectCollection`][box_object_collection] that allows you to iterate over all the [`MetadataTemplate`][metadata_template_class] objects in the collection. + ```python templates = client.get_metadata_templates() for template in templates: @@ -139,6 +138,7 @@ To return the metadata templates available to all enterprises pass in the `global` scope. + ```python templates = client.get_metadata_templates(scope='global) for template in templates: @@ -148,13 +148,13 @@ for template in templates: [get_metadata_templates]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_metadata_templates [box_object_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.pagination.html#boxsdk.pagination.box_object_collection.BoxObjectCollection -Delete Metadata Template ------------------------- +## Delete Metadata Template -To delete a metadata template, call [`template.delete()`][delete]. This method returns `True` to indicate the deletion +To delete a metadata template, call [`template.delete()`][delete]. This method returns `True` to indicate the deletion was successful. + ```python client.metadata_template('enterprise', 'employeeRecord').delete() ``` diff --git a/docs/usage/metadata_cascade_policies.md b/docs/usage/metadata_cascade_policies.md index 94fb03743..a07f42a02 100644 --- a/docs/usage/metadata_cascade_policies.md +++ b/docs/usage/metadata_cascade_policies.md @@ -1,18 +1,16 @@ -Metadata Cascade Policies -========================= +# Metadata Cascade Policies -Metadata Cascade Policies allow the metadata values on a folder to be applied to the files within that folder. A +Metadata Cascade Policies allow the metadata values on a folder to be applied to the files within that folder. A cascade policy associates the folder with a specific metadata template whose instance values on the folder should be cascaded to files within the folder. -> __Note:__ The Metadata Cascade Policy endpoints are currently in beta. Please email +> **Note:** The Metadata Cascade Policy endpoints are currently in beta. Please email > [betas+metadata@box.com](mailto:betas+metadata@box.com) if you would like to enable this beta feature for your -> enterprise. If you do not have this enabled for your enterprise, you will get a 403 error. +> enterprise. If you do not have this enabled for your enterprise, you will get a 403 error. - - [Create a Metadata Cascade Policy](#create-a-metadata-cascade-policy) - [Get Information About a Metadata Cascade Policy](#get-information-about-a-metadata-cascade-policy) - [Get Cascade Policies on a Folder](#get-cascade-policies-on-a-folder) @@ -21,14 +19,14 @@ cascaded to files within the folder. -Create a Metadata Cascade Policy --------------------------------- +## Create a Metadata Cascade Policy To create a metadata cascade policy on a folder, call [`folder.cascade_metadata(metadata_template)`][cascade_metadata] -with the [`MetadataTemplate`][metadata_template_class] whose values should be cascaded within the folder. This +with the [`MetadataTemplate`][metadata_template_class] whose values should be cascaded within the folder. This method returns a [`MetadataCascadePolicy`][cascade_policy_class] object representing the newly-created policy. + ```python folder = client.folder(folder_id='22222') metadata_template = client.metadata_template('enterprise', 'securityClassiciation') @@ -41,16 +39,16 @@ print(f'Folder {cascade_policy.parent.id} has a metadata cascade policy for {cas [metadata_template_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_template.MetadataTemplate [cascade_policy_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_cascade_policy.MetadataCascadePolicy -Get Information About a Metadata Cascade Policy ------------------------------------------------ +## Get Information About a Metadata Cascade Policy To retrieve information about a metadata cascade policy, first call [`client.metadata_cascade_policy(policy_id)`][initializer] with the ID of the cascade policy to initialize the [`MetadataCascadePolicy`][cascade_policy_class] object, then call [`cascade_policy.get()`][get] to retrieve data about -the policy. This method returns a new [`MetadataCascadePolicy`][cascade_policy_class] object with fields populated by +the policy. This method returns a new [`MetadataCascadePolicy`][cascade_policy_class] object with fields populated by data from the API, leaving the original object unmodified. + ```python cascade_policy = client.metadata_cascade_policy('84113349-794d-445c-b93c-d8481b223434').get() print(f'Cascade policy applies to a template owned by enterprise {cascade_policy.owner_enterprise.id}') @@ -59,17 +57,17 @@ print(f'Cascade policy applies to a template owned by enterprise {cascade_policy [initializer]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.metadata_cascade_policy [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Get Cascade Policies on a Folder --------------------------------- +## Get Cascade Policies on a Folder To get a list of the cascade policies applied to a folder, call [`folder.get_metadata_cascade_policies(owner_enterprise=None, limit=None, marker=None, fields=None)`][get_metadata_cascade_policies]. You can optionally pass an [`Enterprise`][enterprise_class] object via the `owner_enterprise` parameter to retrieve cascade policies related to templates for a specific enterprise; if not specified, this defaults to the current -enterprise. This method returns a [`BoxObjectCollection`][box_object_collection] that allows you to iterate over the +enterprise. This method returns a [`BoxObjectCollection`][box_object_collection] that allows you to iterate over the [`MetadataCascadePolicy`][cascade_policy_class] objects in the collection. + ```python cascade_policies = client.folder(folder_id='22222').get_metadata_cascade_policies() for policy in cascade_policies: @@ -80,16 +78,16 @@ for policy in cascade_policies: [enterprise_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.enterprise.Enterprise [box_object_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.pagination.html#boxsdk.pagination.box_object_collection.BoxObjectCollection -Force Apply Cascade Policy --------------------------- +## Force Apply Cascade Policy A Cascade Policy can be forced to re-apply to all files in the associated folder by calling -[`cascade_policy.force_apply(conflict_resolution)`][force_apply] with the conflict resolution strategy to use. If +[`cascade_policy.force_apply(conflict_resolution)`][force_apply] with the conflict resolution strategy to use. If files in the folder already have metadata values that conflict with the ones being force applied from the folder, you -can choose to either preserve the existing values or overwrite them with the folder's values. This method returns +can choose to either preserve the existing values or overwrite them with the folder's values. This method returns `True` to indicate that the force application was successful. + ```python from boxsdk.object.metadata_cascade_policy import CascadePolicyConflictResolution @@ -100,13 +98,13 @@ print('Cascade policy was force applied!') [force_apply]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.metadata_cascade_policy.MetadataCascadePolicy.force_apply -Remove Cascade Policy ---------------------- +## Remove Cascade Policy -A metadata cascade policy can be removed from a folder by calling [`cascade_policy.delete()`][delete]. This method +A metadata cascade policy can be removed from a folder by calling [`cascade_policy.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python client.metadata_cascade_policy(policy_id='84113349-794d-445c-b93c-d8481b223434').delete() print('Cascade policy successfully removed') diff --git a/docs/usage/retention_policy.md b/docs/usage/retention_policy.md index 3a40af603..8b7343a09 100644 --- a/docs/usage/retention_policy.md +++ b/docs/usage/retention_policy.md @@ -1,7 +1,6 @@ -Retention Policies -================== +# Retention Policies -A retention policy blocks permanent deletion of content for a specified amount of time. Admins can create retention +A retention policy blocks permanent deletion of content for a specified amount of time. Admins can create retention policies and then later assign them to specific folders or their entire enterprise. @@ -14,20 +13,20 @@ policies and then later assign them to specific folders or their entire enterpri - [Assign Retention Policy](#assign-retention-policy) - [Get Retention Policy Assignment](#get-retention-policy-assignment) - [Get Retention Policy Assignments](#get-retention-policy-assignments) -- [Get File Version Retentions](#get-file-version-retentions) (deprecated, use [Get Files under Retention for a Retention Policy Assignment](#get-files-under-retention-for-an-assignment) and [Get File Versions under Retention for a Retention Policy Assignment](#get-file-versions-under-retention-for-an-assignment) instead) +- [Get File Version Retentions](#get-file-version-retentions) (deprecated, use [Get Files under Retention for a Retention Policy Assignment](#get-files-under-retention-for-an-assignment) and [Get File Versions under Retention for a Retention Policy Assignment](#get-file-versions-under-retention-for-an-assignment) instead) - [Get Information about a File Version Retention](#get-information-about-a-file-version-retention) - [Get Files under Retention for a Retention Policy Assignment](#get-files-under-retention-for-a-retention-policy-assignment) - [Get File Versions under Retention for a Retention Policy Assignment](#get-file-versions-under-retention-for-a-retention-policy-assignment) -Create Retention Policy ------------------------ +## Create Retention Policy -To create a retention policy object, call [`client.create_retention_policy(policy_name, disposition_action, retention_length, can_owner_extend_retention=None, are_owners_notified=None, custom_notification_recipients=None)`][create_retention_policy]. This will let you create a new indefinite +To create a retention policy object, call [`client.create_retention_policy(policy_name, disposition_action, retention_length, can_owner_extend_retention=None, are_owners_notified=None, custom_notification_recipients=None)`][create_retention_policy]. This will let you create a new indefinite [`RetentionPolicy`][retention_policy_class] object populated with data from the API. + ```python policy_name = 'Test Indefinite Policy Name' disposition_action = 'remove_retention' @@ -35,7 +34,7 @@ indefinite_retention_policy = client.create_retention_policy(policy_name, dispos print(f'Indefinite Retention Policy ID is {indefinite_retention_policy.id} and the policy name is {indefinite_retention_policy.policy_name}') ``` -Alternatively, if you want to create a finite retention policy, you can do so by calling +Alternatively, if you want to create a finite retention policy, you can do so by calling [`client.create_retention_policy(policy_name, disposition_action, retention_length=5)`][create_retention_policy] ```python @@ -49,14 +48,14 @@ print(f'Finite Retention Policy ID is {finite_retention_policy.id} and the polic [create_retention_policy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.create_retention_policy [retention_policy_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.retention_policy.RetentionPolicy -Get Retention Policy --------------------- +## Get Retention Policy -To get a retention policy object, first call [`client.retention_policy(retention_id)`][retention_policy] to construct the -appropriate [`RetentionPolicy`][retention_policy_class] object, and then calling [`retention_policy.get(*, fields=None, headers=None, **kwargs)`][get] +To get a retention policy object, first call [`client.retention_policy(retention_id)`][retention_policy] to construct the +appropriate [`RetentionPolicy`][retention_policy_class] object, and then calling [`retention_policy.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`RetentionPolicy`][retention_policy_class] object populated with data from the API. + ```python retention_policy = client.retention_policy(retention_id='12345').get() print(f'Retention Policy ID is {retention_policy.id} and the name is {retention_policy.policy_name}') @@ -65,14 +64,14 @@ print(f'Retention Policy ID is {retention_policy.id} and the name is {retention_ [retention_policy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.retention_policy [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Get Retention Policies ----------------------- +## Get Retention Policies -To retrieve all retention policies for an enterprise, call [`client.get_retention_policies`][get_retention_policies]. -This method returns a `BoxObjectCollection` that allows you to iterate over the +To retrieve all retention policies for an enterprise, call [`client.get_retention_policies`][get_retention_policies]. +This method returns a `BoxObjectCollection` that allows you to iterate over the [`Retention Policy`][retention_policy_class] objects in the collection. + ```python retention_policies = client.get_retention_policies() for policy in retention_policies: @@ -81,14 +80,14 @@ for policy in retention_policies: [get_retention_policies]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_retention_policies -Update Retention Policy ------------------------ +## Update Retention Policy To update a retention policy object, calling [`retention_policy.update_info(data=policy_update)`][update_info] with -a `dict` of properties to update on the retention policy. This method returns a newly updates +a `dict` of properties to update on the retention policy. This method returns a newly updates [`RetentionPolicy`][retention_policy_class] object, leaving the original object unmodified. + ```python policy_update = {'policy_name': 'New Policy Name',} updated_retention_policy = client.retention_policy(retention_id='12345').update_info(data=policy_update) @@ -97,13 +96,13 @@ print(f'Retention Policy ID is {updated_retention_policy.id} and the new policy [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Assign Retention Policy ------------------------ +## Assign Retention Policy -To assign a retention policy, call [`retention_policy.assign(folder)`][assign] will create a new +To assign a retention policy, call [`retention_policy.assign(folder)`][assign] will create a new [`RetentionPolicyAssignment`][retention_policy_assignment_class] object populated with data from the API. + ```python folder = client.folder(folder_id='1111') assignment = client.retention_policy(retention_id='12345').assign(folder) @@ -113,15 +112,15 @@ print(f'Assignment ID is {assignment.id} and it is assigned by {assignment.assig [retention_policy_assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.retention_policy_assignment.RetentionPolicyAssignment [assign]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.retention_policy.RetentionPolicy.assign -Get Retention Policy Assignment -------------------------------- +## Get Retention Policy Assignment -To get a retention policy object, first call [`client.retention_policy_assignment(assignment_id)`][retention_policy_assignment] -to construct the appropriate [`Retention Policy Assignment`][retention_policy_assignment_class] object, and then calling -[`retention_policy_assignment.get(*, fields=None, headers=None, **kwargs)`][get] will return the +To get a retention policy object, first call [`client.retention_policy_assignment(assignment_id)`][retention_policy_assignment] +to construct the appropriate [`Retention Policy Assignment`][retention_policy_assignment_class] object, and then calling +[`retention_policy_assignment.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`Retention Policy Assignment`][retention_policy_assignment_class] object populated with data from the API. + ```python assignment = client.retention_policy_assignment('12345').get() print(f'Assignment id is {assignment.id} and it is assigned by {assignment.assigned_by.name}') @@ -131,22 +130,22 @@ print(f'Assignment id is {assignment.id} and it is assigned by {assignment.assig [retention_policy_assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.retention_policy_assignment.RetentionPolicyAssignment [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Get Retention Policy Assignments --------------------------------- +## Get Retention Policy Assignments To retrieve all retention policy assignments for an enterprise, call -[`retention_policy.assignments(assignment_type=None, limit=None, marker=None, fields=None)`][get_assignments] -will return a `BoxObjectCollection` that allows you to iterate over the +[`retention_policy.assignments(assignment_type=None, limit=None, marker=None, fields=None)`][get_assignments] +will return a `BoxObjectCollection` that allows you to iterate over the [`RetentionPolicyAssignment`][retention_policy_assignment_class] objects in the collection. + ```python assignments = client.retention_policy(retention_id='12345').assignments(limit=10) for assignment in assignments: print(f'Assignment ID is {assignment.id} and it is assigned by {assignment.assigned_by.name}') ``` -Alternatively, you can also specify the `type` of assignment to retrieve with +Alternatively, you can also specify the `type` of assignment to retrieve with [`retention_policy.assignments(assignment_type='folder')`][get_assignments]. ```python @@ -157,14 +156,14 @@ for assignment in assignments: [get_assignments]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.retention_policy.assignments -Get File Version Retentions ---------------------------- +## Get File Version Retentions -To retrieve all file version retentions, call [`client.get_file_version_retentions(target_file=None, file_version=None, policy=None, disposition_action=None, disposition_before=None, disposition_after=None, limit=None, marker=None, fields=None)`][get_file_version_retentions]. This method will return a -`BoxObjectCollection` that allows you to iterate over the [`FileVersionRetention`][file_version_retention_class] +To retrieve all file version retentions, call [`client.get_file_version_retentions(target_file=None, file_version=None, policy=None, disposition_action=None, disposition_before=None, disposition_after=None, limit=None, marker=None, fields=None)`][get_file_version_retentions]. This method will return a +`BoxObjectCollection` that allows you to iterate over the [`FileVersionRetention`][file_version_retention_class] objects in the collection. + ```python retentions = client.get_file_version_retentions() for retention in retentions: @@ -174,16 +173,15 @@ for retention in retentions: [get_file_version_retentions]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client,Client.get_file_version_retentions [file_version_rention_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file_version_retention.FileVersionRetention +## Get Information about a File Version Retention -Get Information about a File Version Retention ----------------------------------------------- - -To get a file version retention object, first call [`client.file_version_retention(retention_id)`][file_version_retention] -to construct the appropriate [`File Version Retention`][file_version_retention_class] object, and then calling -[`file_version_retention.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`FileVersionRetention`][file_version_retention] +To get a file version retention object, first call [`client.file_version_retention(retention_id)`][file_version_retention] +to construct the appropriate [`File Version Retention`][file_version_retention_class] object, and then calling +[`file_version_retention.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`FileVersionRetention`][file_version_retention] object populated with data from the API. + ```python retention_info = client.file_version_retention(retention_id='12345').get() print(f'The file version retention ID is {retention.id} and the data time applied at is {retention.applied_at}') @@ -193,15 +191,15 @@ print(f'The file version retention ID is {retention.id} and the data time applie [file_version_retention_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file_version_retention.FileVersionRetention [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Get Files under Retention for a Retention Policy Assignment ---------------------------- +## Get Files under Retention for a Retention Policy Assignment -To retrieve all files under retention for a Retention Policy Assignment, -call [`retention_policy_assignment.get_files_under_retention(limit=None, marker=None)`][get-files-under-retention-for-an-assignment]. +To retrieve all files under retention for a Retention Policy Assignment, +call [`retention_policy_assignment.get_files_under_retention(limit=None, marker=None)`][get-files-under-retention-for-an-assignment]. This method will return a `MarkerBasedObjectCollection` that allows you to iterate over the [`File`][file_class] objects in the collection. + ```python retention_policy_assignment = client.retention_policy_assignment(assignment_id='12345').get() files_under_retention = retention_policy_assignment.get_files_under_retention() @@ -212,9 +210,7 @@ for file in files_under_retention: [get-files-under-retention-for-an-assignment]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#module-boxsdk.object.retention_policy_assignment [file_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#module-boxsdk.object.file - -Get File Versions under Retention for a Retention Policy Assignment ---------------------------- +## Get File Versions under Retention for a Retention Policy Assignment To retrieve all file versions under retention for a retention policy assignment, call [`retention_policy_assignment.get_file_versions_under_retention(limit=None, marker=None)`][get-file-versions-under-retention-for-an-assignment]. @@ -222,6 +218,7 @@ This method will return a `MarkerBasedObjectCollection` that allows you to itera objects in the collection. + ```python retention_policy_assignment = client.retention_policy_assignment(assignment_id='12345').get() file_versions_under_retention = retention_policy_assignment.get_file_versions_under_retention() diff --git a/docs/usage/search.md b/docs/usage/search.md index 0bcc00a20..cea2c880d 100644 --- a/docs/usage/search.md +++ b/docs/usage/search.md @@ -1,19 +1,18 @@ -Search -====== +# Search -Search provides a powerful way of finding items that are accessible by a single user or an entire +Search provides a powerful way of finding items that are accessible by a single user or an entire enterprise. - [Search for Content](#search-for-content) - [Metadata Query](#metadata-query) -Search for Content ------------------- +## Search for Content To get a list of items matching a search query, call [`search.query(query, limit=None, offset=0, ancestor_folders=None, file_extensions=None, metadata_filters=None, result_type=None, content_types=None, scope=None, created_at_range=None, updated_at_range=None, size_range=None, owner_users=None, trash_content=None, fields=None, sort=None, direction=None, **kwargs)`][query] will return an `Iterable` that allows you to iterate over the [`Item`][item_class] objects in the collection. + ```python items = client.search().query(query='TEST QUERY', limit=100, file_extensions=['pdf', 'doc']) for item in items: @@ -25,12 +24,12 @@ for item in items: ### Metadata Search -To filter by metadata in your search, first create [`MetadataSearchFilter`][metadata_search_filter] object with the -specified `template_key` and `scope` as well as adding filter, `field_key` and `value` with -[`metadata_filter.add_value_based_filter(field_key, value)`][add_value_based_filter]. Next, create a -[`MetadataSearchFilters`][metadata_search_filters] object and call [`metadata_filters.add_filter(metadata_filter)`][add_filter] -and pass in the [`MetadataSearchFilter`][metadata_search_filter] object created earlier. Finally, call -[`search.query(query, metadata_filters=None, **kwargs)`][query] with [`MetadataSearchFilters`][metadata_search_filters] +To filter by metadata in your search, first create [`MetadataSearchFilter`][metadata_search_filter] object with the +specified `template_key` and `scope` as well as adding filter, `field_key` and `value` with +[`metadata_filter.add_value_based_filter(field_key, value)`][add_value_based_filter]. Next, create a +[`MetadataSearchFilters`][metadata_search_filters] object and call [`metadata_filters.add_filter(metadata_filter)`][add_filter] +and pass in the [`MetadataSearchFilter`][metadata_search_filter] object created earlier. Finally, call +[`search.query(query, metadata_filters=None, **kwargs)`][query] with [`MetadataSearchFilters`][metadata_search_filters] object passed in. ```python @@ -56,6 +55,7 @@ To get a list of items matching a search query, including items that a user migh to iterate over the search result objects in the collection. + ```python search_results = client.search().query_with_shared_links(query='TEST QUERY', limit=100, file_extensions=['pdf', 'doc']) for search_result in search_results: @@ -64,17 +64,16 @@ for search_result in search_results: [query_with_shared_links]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.search.Search.query_with_shared_links -Metadata Query --------------- -To search using SQL-like syntax to return items that match specific metadata, call `search.metadata_query(from_template, ancestor_folder_id, query=None, query_params=None, use_index=None, order_by=None, marker=None, limit=None, fields=None)` - -**Note:** paramter `use_index` is deprecated and if provided, will be ignored. The current way to create an index is to contact the MDQ team directly via email. MDQ will now use static query analysis to use the index that is most efficient for the call. +## Metadata Query +To search using SQL-like syntax to return items that match specific metadata, call `search.metadata_query(from_template, ancestor_folder_id, query=None, query_params=None, use_index=None, order_by=None, marker=None, limit=None, fields=None)` +**Note:** paramter `use_index` is deprecated and if provided, will be ignored. The current way to create an index is to contact the MDQ team directly via email. MDQ will now use static query analysis to use the index that is most efficient for the call. By default, this method returns only the most basic info about the items for which the query matches. To get additional fields for each item, including any of the metadata, use the fields parameter. + ```python from_template = 'enterprise_12345.someTemplate' ancestor_folder_id = '5555' diff --git a/docs/usage/sign_requests.md b/docs/usage/sign_requests.md index 0c61f3f02..471cb7c61 100644 --- a/docs/usage/sign_requests.md +++ b/docs/usage/sign_requests.md @@ -1,5 +1,4 @@ -Sign Requests -================== +# Sign Requests Sign Requests are used to request e-signatures on documents from signers. A Sign Request can refer to one or more Box Files and can be sent to one or more Box Sign Request Signers. @@ -7,7 +6,6 @@ A Sign Request can refer to one or more Box Files and can be sent to one or more - - [Create Sign Request](#create-sign-request) - [Get all Sign Requests](#get-all-sign-requests) - [Get Sign Request by ID](#get-sign-request-by-id) @@ -16,8 +14,7 @@ A Sign Request can refer to one or more Box Files and can be sent to one or more -Create Sign Request ------------------------- +## Create Sign Request The [`client.create_sign_request_v2(signers, files=None, parent_folder_id=None, prefill_tags=None, are_reminders_enabled=None, are_text_signatures_enabled=None, days_valid=None, email_message=None, email_subject=None, external_id=None, is_document_preparation_needed=None, redirect_url=None, declined_redirect_url=None, template_id=None)`][create-sign-request] method will create a Sign Request. You need to provide at least one file and up to 10 files (from which the signing document will be created) or template_id of the sign request template. You need to include at least one signer that will receive the Sign Request. @@ -56,18 +53,18 @@ new_sign_request = client.create_sign_request_v2(signers, template_id='12345') print(f'(Sign Request ID: {new_sign_request.id})') ``` -If you set ```isDocumentPreparationNeeded``` flag to true, you need to visit ```prepareUrl``` before the Sign Request will be sent. -For more information on ```isDocumentPreparationNeeded``` and the other parameters available, please refer to the [developer documentation](https://developer.box.com/guides/sign-request/). +If you set `isDocumentPreparationNeeded` flag to true, you need to visit `prepareUrl` before the Sign Request will be sent. +For more information on `isDocumentPreparationNeeded` and the other parameters available, please refer to the [developer documentation](https://developer.box.com/guides/sign-request/). [create-sign-request]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.create_sign_request -Get All Sign Requests ------------------------- +## Get All Sign Requests Calling the [`client.get_sign_requests()`][get-all-sign-requests] will return an iterable that will page through all the Sign Requests. This method offers `limit` and `fields` parameters. The `limit` parameter specifies the maximum number of items to be returned in a single response. The `fields` parameter is used to specify what additional properties should be returned on the return object. For more information on what `fields` are available, please refer to the [developer documentation](https://developer.box.com/guides/box-sign/). + ```python sign_requests = client.get_sign_requests() for sign_request in sign_requests: @@ -76,14 +73,14 @@ for sign_request in sign_requests: [get-all-sign-requests]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_sign_requests -Get Sign Request by ID ------------------------- +## Get Sign Request by ID Calling [`client.sign_request(sign_request_id)`][get-sign-request-by-id] will return an object containing information about the Sign Request. The `fields` parameter is used to specify what additional properties should be returned in the return object. + ```python sign_request = client.sign_request(sign_request_id='12345').get() print(f'Sign Request ID is {sign_request.id}') @@ -91,12 +88,12 @@ print(f'Sign Request ID is {sign_request.id}') [get-sign-request-by-id]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.sign_request -Cancel Sign Request ------------------------- +## Cancel Sign Request Calling [`sign_requests.cancel()`][cancel-sign-request] will cancel a created Sign Request. + ```python sign_request = client.sign_request(sign_request_id='12345') cancelled_sign_request = sign_request.cancel() @@ -105,13 +102,13 @@ print(f'Cancelled Sign Request status is {cancelled_sign_request.status}') [cancel-sign-request]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.retention_policy.SignRequest.cancel -Resend Sign Request ------------------------- +## Resend Sign Request Calling [`sign_requests.resend()`][resend-sign-request] will resend a Sign Request to all signers that have not signed it yet. There is an 10-minute cooling-off period between re-sending reminder emails. + ```python sign_request = client.sign_request(sign_request_id='12345') sign_request.resend() diff --git a/docs/usage/sign_templates.md b/docs/usage/sign_templates.md index 52ee09337..208719f01 100644 --- a/docs/usage/sign_templates.md +++ b/docs/usage/sign_templates.md @@ -1,5 +1,4 @@ -Sign Templates -============== +# Sign Templates Sign Templates are reusable templates that can be used to create Sign Requests. For now, Sign Templates can only be created through the Box web application. @@ -11,12 +10,12 @@ Sign Templates are reusable templates that can be used to create Sign Requests. -Get All Sign Templates ----------------------- +## Get All Sign Templates Calling the [`client.get_sign_templates()`][get-all-sign-templates] method will return an iterable that will page through all the Sign Templates. This method offers `limit` parameter. The `limit` parameter specifies the maximum number of items to be returned in a single response. + ```python sign_templates = client.get_sign_templates() for sign_template in sign_templates: @@ -25,12 +24,12 @@ for sign_template in sign_templates: [get-all-sign-templates]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_sign_templates -Get Sign Template by ID ------------------------ +## Get Sign Template by ID Calling the [`client.get_sign_template(template_id)`][get-sign-template] method will return a Sign Template object. + ```python sign_template = client.get_sign_template('12345') print(f'(Sign Template ID: {sign_template.id})') diff --git a/docs/usage/storage_policy.md b/docs/usage/storage_policy.md index 9e40911f6..07fe20884 100644 --- a/docs/usage/storage_policy.md +++ b/docs/usage/storage_policy.md @@ -1,14 +1,12 @@ -Storage Policies -================ +# Storage Policies Allows the enterprise admin to manage the Storage Policies for users in their enterprise. Used for an enterprise to decide storage location for users based on -where they work/reside. +where they work/reside. - - [Get Storage Policy](#get-storage-policy) - [List Available Storage Policies](#list-available-storage-policies) - [Assign a Storage Policy to a User](#assign-a-storage-policy-to-a-user) @@ -18,14 +16,14 @@ where they work/reside. -Get Storage Policy ------------------- +## Get Storage Policy -To get a storage policy object, first call [`client.storage_policy(policy_id)`][storage_policy] to construct the -appropriate [`Storage Policy`][storage_policy_class] object, and then calling [`storage_policy.get(*, fields=None, headers=None, **kwargs)`][get] +To get a storage policy object, first call [`client.storage_policy(policy_id)`][storage_policy] to construct the +appropriate [`Storage Policy`][storage_policy_class] object, and then calling [`storage_policy.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`StoragePolicy`][storage_policy_class] object populated with data from the API. + ```python storage_policy = client.storage_policy(policy_id='12345').get() print(f'Storage Policy ID is {storage_policy.id} and name is {storage_policy.name}') @@ -35,14 +33,14 @@ print(f'Storage Policy ID is {storage_policy.id} and name is {storage_policy.nam [storage_policy_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.storage_policy.StoragePolicy [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -List Available Storage Policies -------------------------------- +## List Available Storage Policies -To retrieve all storage policies for an enterprise, call [`client.get_storage_policies(limit=None, fields=None)`][get_storage_policies]. -This method returns a `BoxObjectCollection` that allows you to iterate over the [`StoragePolicy`][storage_policy_class] +To retrieve all storage policies for an enterprise, call [`client.get_storage_policies(limit=None, fields=None)`][get_storage_policies]. +This method returns a `BoxObjectCollection` that allows you to iterate over the [`StoragePolicy`][storage_policy_class] objects in the collection. + ```python storage_policies = client.get_storage_policies(limit=100) for storage_policy in storage_policies: @@ -51,13 +49,13 @@ for storage_policy in storage_policies: [get_storage_policies]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_storage_policies -Assign a Storage Policy to a User ---------------------------------- +## Assign a Storage Policy to a User -To assign a storage policy to a user, call [`storage_policy.assign(user)`][assign] will create a +To assign a storage policy to a user, call [`storage_policy.assign(user)`][assign] will create a [`StoragePolicyAssignment`][storage_policy_assignment_class] object with data populated from the API. + ```python user = client.user(user_id='12345') assignment = client.storage_policy(policy_id='56781').assign(user) @@ -65,7 +63,7 @@ print(f'Assignment ID is {assignment.id} and the assignee id is {assignment.assi ``` If you know the user does not have a storage policy assigned you can directly create a storage policy assignment by calling -[`storage_policy.create_assignment(user)`][create_assignment] will create a [StoragePolicyAssignment][storage_policy_assignment_class] +[`storage_policy.create_assignment(user)`][create_assignment] will create a [StoragePolicyAssignment][storage_policy_assignment_class] object with data populated from the API. ```python @@ -75,11 +73,12 @@ print(f'Storage Policy Assignment ID is {assignment.id} and the assignee ID is { ``` If the user already has an assignment, you can call [storage_policy_assignment.update_info(data=updated_storage_policy)][update_info] -with a `dict` of properties to update on the storage policy assignment. This method returns a newly update -[`StoragePolicyAssignment`][storage_policy_assignment] object with data populated from the API, leaving the original +with a `dict` of properties to update on the storage policy assignment. This method returns a newly update +[`StoragePolicyAssignment`][storage_policy_assignment] object with data populated from the API, leaving the original object unmodified. + ```python updated_storage_policy = {'storage_policy': {'type': 'storage_policy', 'id': '12345'}} updated_assignment = client.storage_policy_assignment(assignment_id='ZW50ZXJwcmldfgeV82MDMwMDQ=').update_info(data=updated_storage_policy) @@ -91,15 +90,15 @@ print(f'Update storage policy ID is {updated_assignment.storage_policy.id}') [storage_policy_assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.storage_policy_assignment.StoragePolicyAssignment [assign]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.storage_policy.StoragePolicy.assign -Get Assignment Information about a Storage Policy Assignment ------------------------------------------------------------- +## Get Assignment Information about a Storage Policy Assignment -To get a storage policy assignment object, first call [`client.storage_policy_assignment(assignment_id)`][storage_policy_assignment] -to construct the appropriate [`Storage Policy Assignment`][storage_policy_assignment_class] object, and then calling -[`storage_policy_assignment.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`StoragePolicyAssignment`][storage_policy_assignment_class] +To get a storage policy assignment object, first call [`client.storage_policy_assignment(assignment_id)`][storage_policy_assignment] +to construct the appropriate [`Storage Policy Assignment`][storage_policy_assignment_class] object, and then calling +[`storage_policy_assignment.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`StoragePolicyAssignment`][storage_policy_assignment_class] object populated with data from the API. + ```python assignment = client.storage_policy_assignment(assignment_id='12345').get() print(f'Assignment ID is {assignment.id} and the storage policy ID is {assignment.storage_policy.id}') @@ -109,13 +108,13 @@ print(f'Assignment ID is {assignment.id} and the storage policy ID is {assignmen [storage_policy_assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.storage_policy_assignment.StoragePolicyAssignment [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Get Assignment for User -------------------------- +## Get Assignment for User -To get a storage policy assignment object for a user, calling [`user.get_storage_policy_assignment()`][user_assignment] will +To get a storage policy assignment object for a user, calling [`user.get_storage_policy_assignment()`][user_assignment] will return the [`StoragePolicyAssignment`][storage_policy_assignment_class] object populated with data from the API. + ```python assignment = client.user(user_id='12345').get_storage_policy_assignment() print(f'Assignment ID is {assignment.id} and the storage policy ID is {assignment.storage_policy.id}') @@ -124,13 +123,13 @@ print(f'Assignment ID is {assignment.id} and the storage policy ID is {assignmen [user_assignment]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User.get_storage_policy_assignment [storage_policy_assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User.get_storage_policy_assignment -Delete Assignment ------------------ +## Delete Assignment -To delete a storage policy assignment, call [`storage_policy_assignment.delete()`][delete]. This method returns `True` +To delete a storage policy assignment, call [`storage_policy_assignment.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python client.storage_policy_assignment(assignment_id='12345').delete() print('The storage policy assignment was successfully delete!') diff --git a/docs/usage/task.md b/docs/usage/task.md index 151ed84df..e822e8fdb 100644 --- a/docs/usage/task.md +++ b/docs/usage/task.md @@ -1,12 +1,10 @@ -Tasks -===== +# Tasks Tasks enable file-centric workflows in Box. User can create tasks on files and assign them to collaborators on Box. - - [Get a Task's Information](#get-a-tasks-information) - [List Tasks on File](#list-tasks-on-file) - [Add Task to File](#add-task-to-file) @@ -21,14 +19,14 @@ Tasks enable file-centric workflows in Box. User can create tasks on files and a -Get a Task's Information ------------------------- +## Get a Task's Information -To get a task object, first call [`client.task(task_id)`][task] to construct the appropriate [`Task`][task_class] -object, and then calling [`task.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`Task`][task_class] object populated with data +To get a task object, first call [`client.task(task_id)`][task] to construct the appropriate [`Task`][task_class] +object, and then calling [`task.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`Task`][task_class] object populated with data from the API, leaving the original object unmodified. + ```python task = client.task(task_id='12345').get() print(f'Task ID is {task.id} and the type is {task.type}') @@ -38,13 +36,13 @@ print(f'Task ID is {task.id} and the type is {task.type}') [task_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task.Task [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -List Tasks on File ------------------- +## List Tasks on File -To retrieve all tasks on a file, call [`file.get_tasks(fields=None)`]['get_tasks'] will return a `BoxObjectCollection` +To retrieve all tasks on a file, call [`file.get_tasks(fields=None)`]['get_tasks'] will return a `BoxObjectCollection` that allows you to iterate over the [`Task`][task_class] objects in the collection. + ```python tasks = client.file(file_id='11111').get_tasks() for task in tasks: @@ -54,13 +52,13 @@ for task in tasks: [get_tasks]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.get_tasks() [task_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task.Task -Add Task to File ----------------- +## Add Task to File -To create a single task for a single user on a single file, call [`file.create_task(message=None, due_at=None)`][create_task] +To create a single task for a single user on a single file, call [`file.create_task(message=None, due_at=None)`][create_task] will return a newly created [`Task`][task_class] object populated with data from the API. + ```python message = 'Please review this' due_at = "2014-04-03T11:09:43-07:00" @@ -70,13 +68,13 @@ print(f'Task message is {task.message} and it is due at {task.due_at}') [create_task]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.file.File.create_Task -Update Task Info ----------------- +## Update Task Info To update a task object, first call [`task.update_info(data=task_update)`][update_info] with a `dict` of properties to update on the task. This method returns a newly updated [`Task`][task_class] object, leaving the original object unmodified. + ```python task_update = {'message': 'New Message', 'due_at': '2014-04-03T11:09:43-10:00'} updated_task = client.task(task_id='12345').update_info(data=task_update) @@ -86,13 +84,13 @@ print(f'New task message is {updated_task.message} and the new due time is {upda [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info [task_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task.Task -Delete a Task -------------- +## Delete a Task -To delete a task, first call [`client.task(task_id)`][task] to construct the appropriate task object, and then call +To delete a task, first call [`client.task(task_id)`][task] to construct the appropriate task object, and then call [`task.delete()`][delete]. This method returns `True` to indicate that the deleteion was successful. + ```python client.client.task('12345').delete() print('The task was successfully delete!') @@ -101,15 +99,14 @@ print('The task was successfully delete!') [task]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.task [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete +## Assign a Task -Assign a Task --------------- - -To assign a task object, first call [`client.task(task_id)`][task] to construct the appropriate task object, then call -[`task.assign(user)`][assign] will return an [`Task Assignment`][assignment_class] object, populated with data +To assign a task object, first call [`client.task(task_id)`][task] to construct the appropriate task object, then call +[`task.assign(user)`][assign] will return an [`Task Assignment`][assignment_class] object, populated with data from the API. + ```python user = client.user(user_id='11111') assignment = client.task(task_id='12345').assign(user) @@ -120,11 +117,10 @@ print(f'Assignment ID is {assignment.id} and is assigned to user {assignment.ass [assign]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task.Task.assign [assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task_assignment.TaskAssignment -Assign a Task with User Login ------------------------------ +## Assign a Task with User Login -To assign a task object with a user login, first call [`task.assign_with_login(login)`][assign_with_login] with a -`unicode` value for user login. This method will return a [`TaskAssignment`][assignment_class] object, populated with +To assign a task object with a user login, first call [`task.assign_with_login(login)`][assign_with_login] with a +`unicode` value for user login. This method will return a [`TaskAssignment`][assignment_class] object, populated with data from the API. ```python @@ -135,15 +131,15 @@ print(f'Assignment ID is {assignment.id} and the assignee is {assignment.assigne [assign_with_login]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task_assignment.assign_with_login [assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task_assignment.TaskAssignment -List Task Assignments ---------------------- +## List Task Assignments -To retrieve all assignments for an enterprise, first call [`client.task(task_id)`][task] to construct the appropriate -task object. Then call ['task.get_assignments(fields=None)'][get_assignments]. This method returns a -`BoxObjectCollection` that allows you to iterate over the ['TaskAssignment'][assignment_class] objects in the +To retrieve all assignments for an enterprise, first call [`client.task(task_id)`][task] to construct the appropriate +task object. Then call ['task.get_assignments(fields=None)'][get_assignments]. This method returns a +`BoxObjectCollection` that allows you to iterate over the ['TaskAssignment'][assignment_class] objects in the collection. + ```python assignments = client.task(task_id='12345').get_assignments() for assignment in assignments: @@ -153,14 +149,14 @@ for assignment in assignments: [task]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.task [get_assignments]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task.Task.get_assignments -Get Information about Task Assignment -------------------------------------- +## Get Information about Task Assignment -To get a task assignment object, first call [`client.task_assignment(assignment_id)`][assignment] to construct the -appropriate [`TaskAssignment`][assignment_class] object, and then calling ['task_assignment.get(*, fields=None, headers=None, **kwargs)'][get] +To get a task assignment object, first call [`client.task_assignment(assignment_id)`][assignment] to construct the +appropriate [`TaskAssignment`][assignment_class] object, and then calling ['task_assignment.get(\*, fields=None, headers=None, \*\*kwargs)'][get] will return the [`TaskAssignment`][task_assignment] object populated with data from the API. + ```python assignment= client.task_assignment('12345').get() print(f'Assignment ID is {assignment.id} and assignment type is {assignment.type}') @@ -170,14 +166,14 @@ print(f'Assignment ID is {assignment.id} and assignment type is {assignment.type [assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task_assignment.TaskAssignment [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Update Task Assignment ----------------------- +## Update Task Assignment -To update a task assignment object, call [`assignment.update_info(data=updated_task)`][update_info] -with a `dict` of properties to update on a task assignment. This method returns a newly update +To update a task assignment object, call [`assignment.update_info(data=updated_task)`][update_info] +with a `dict` of properties to update on a task assignment. This method returns a newly update [`TaskAssignment`][assignment_class] object, leaving the original object unmodified. + ```python from boxsdk.object.task_assignment import ResolutionState updated_task = {'resolution_state': ResolutionState.APPROVED} @@ -186,6 +182,7 @@ print(f'Assignment ID is {updated_assignment.id} and resolution state is {update ``` + ```python updated_task = {'message': 'new message'} updated_assignment = client.task_assignment(assignment_id='12345').update_info(data=updated_task) @@ -195,13 +192,13 @@ print(f'Assignment ID is {updated_assignment.id} and message is {updated_task.me [assignment_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.task_assignment.TaskAssignment [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Delete Task Assignment ----------------------- +## Delete Task Assignment -To delete a task assignment, call [`task_assignment.delete()`][delete]. This method returns `True` to indicate that the +To delete a task assignment, call [`task_assignment.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python client.task_assignment(assignment_id='12345').delete() print('The task assignment was successfully delete!') diff --git a/docs/usage/terms_of_service.md b/docs/usage/terms_of_service.md index ae8ca9361..ce1a1f51e 100644 --- a/docs/usage/terms_of_service.md +++ b/docs/usage/terms_of_service.md @@ -1,5 +1,4 @@ -Terms of Service -================ +# Terms of Service Terms of Service allows Box Admins to configure a custom Terms of Service for end users to accept/re-accept/decline for platform applications @@ -7,7 +6,6 @@ accept/re-accept/decline for platform applications - - [Terms of Service](#terms-of-service) - [Create a Terms of Service](#create-a-terms-of-service) - [Edit a Terms of Service](#edit-a-terms-of-service) @@ -18,18 +16,18 @@ accept/re-accept/decline for platform applications -Create a Terms of Service -------------------------- +## Create a Terms of Service A Terms of Service can be created in an enterprise. Please note that only two can be created. One external and one managed. If a terms of service already exists please use the update call to change the current terms of service. -To create a Terms of Service object, calling [`client.create_terms_of_service(status, tos_type, text)`][create] will let -you create a new [`TermsOfService`][terms_of_service_class] object with the specified status, type, and text. This +To create a Terms of Service object, calling [`client.create_terms_of_service(status, tos_type, text)`][create] will let +you create a new [`TermsOfService`][terms_of_service_class] object with the specified status, type, and text. This method will return a newly created [`TermsOfService`][terms_of_service_class] object populated with data from the API. + ```python from boxsdk.object.terms_of_service import TermsOfServiceType, TermsOfServiceStatus terms_of_service = client.create_terms_of_service(TermsOfServiceStatus.ENABLED,TermsOfServiceType.MANAGED, 'Example Text') @@ -39,14 +37,14 @@ print(f'Terms of Service status is {terms_of_service.status} and the message is [create]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.create_terms_of_service [terms_of_service_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.terms_of_service.TermsOfService -Edit a Terms of Service ------------------------ +## Edit a Terms of Service To update a terms of service object, first call [`terms_of_service.update_info(data=update_object)`][update_info] with -a `dict` of properties to update on the terms of service. This method returns a newly updated [`TermsOfService`][terms_of_service] +a `dict` of properties to update on the terms of service. This method returns a newly updated [`TermsOfService`][terms_of_service] object, leaving the original object unmodified. + ```python update_object = {'text': 'New Text'} updated_tos = client.terms_of_service(tos_id='12345').update_info(data=update_object) @@ -57,14 +55,14 @@ print(f'The updated message for your custom terms of service is {updated_tos.tex [terms_of_service_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.terms_of_service.TermsOfService [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Get Terms of Service --------------------- +## Get Terms of Service -To get a terms of service object, call [`client.terms_of_service(service_id)`][terms_of_service] to construct the -appropriate [`TermsOfService`][terms_of_service_class], and then calling [`terms_of_service.get(*, fields=None, headers=None, **kwargs)`][get] +To get a terms of service object, call [`client.terms_of_service(service_id)`][terms_of_service] to construct the +appropriate [`TermsOfService`][terms_of_service_class], and then calling [`terms_of_service.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`TermsOfService`][terms_of_service_class] object populated with data from the API. + ```python terms_of_service = client.terms_of_service(tos_id='12345').get() print(f'Terms of Service ID is {terms_of_service.id} and the message is {terms_of_service.text}') @@ -74,15 +72,15 @@ print(f'Terms of Service ID is {terms_of_service.id} and the message is {terms_o [terms_of_service_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.terms_of_service.TermsOfService [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -List Terms of Service ---------------------- +## List Terms of Service -To retrieve all terms of service for an enterprise, call -[`client.get_terms_of_services(limit=None, marker=None, fields=None)`][get_terms_of_services]. This method returns a -`BoxObjectCollection` that allows you to iterate over the [`TermOfService`][terms_of_service_class] objects in the +To retrieve all terms of service for an enterprise, call +[`client.get_terms_of_services(limit=None, marker=None, fields=None)`][get_terms_of_services]. This method returns a +`BoxObjectCollection` that allows you to iterate over the [`TermOfService`][terms_of_service_class] objects in the collection. + ```python terms_of_services = client.get_terms_of_services() for terms_of_service in terms_of_services: @@ -91,39 +89,40 @@ for terms_of_service in terms_of_services: [get_terms_of_services]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.terms_of_service -Accept or Decline a Terms of Service ------------------------------------- +## Accept or Decline a Terms of Service -To accept or decline a terms of service, calling [`terms_of_service.set_user_status(is_accepted, user)`][set_user_status] -will allow you to create a newly updated [`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object -populated with data from the API, leaving the original object umodified if a [`TermsOfService`][terms_of_service_class] -object already exists for a user. If the user does not have a [`TermsOfService`][terms_of_service_class] object -assigned then [`terms_of_service.set_user_status(is_accepted, user)`][set_user_status] will create a new +To accept or decline a terms of service, calling [`terms_of_service.set_user_status(is_accepted, user)`][set_user_status] +will allow you to create a newly updated [`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object +populated with data from the API, leaving the original object umodified if a [`TermsOfService`][terms_of_service_class] +object already exists for a user. If the user does not have a [`TermsOfService`][terms_of_service_class] object +assigned then [`terms_of_service.set_user_status(is_accepted, user)`][set_user_status] will create a new [`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object populated with data from the API. + ```python user = client.user(user_id='22222') user_status = client.terms_of_service(tos_id='12345').set_user_status(is_accepted=True, user=user) print(f'User status ID is {user_status.id} and the accepted status is {user_status.is_accepted}') ``` -It is important to note that regardless of whether the user has taken action on this terms of service. This will create +It is important to note that regardless of whether the user has taken action on this terms of service. This will create and update the user status on the terms of service. -Note that this example will make multiple API calls, if you know that your user has already accepted or decline a -Terms of Service and you wish to change their status, call [`terms_of_service_user_status.update_info(data=data_to_update)`][update_info] -with a `dict` of properties to update on the terms of service user status. This method returns a newly updated +Note that this example will make multiple API calls, if you know that your user has already accepted or decline a +Terms of Service and you wish to change their status, call [`terms_of_service_user_status.update_info(data=data_to_update)`][update_info] +with a `dict` of properties to update on the terms of service user status. This method returns a newly updated [`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object, leaving the original object unmodified. + ```python user_status = client.terms_of_service_user_status(tos_user_status_id='12345').update_info(data={'is_accepted': True}) print(f'Terms of Service User Status ID is {user_status.id} and the accepted status is {user_status.is_accepted}') ``` -It is important to note that this will accept or decline a custom terms of service for a user. For a user that has taken -action in this terms of service, this will update their status. If the user has never taken action on this terms of +It is important to note that this will accept or decline a custom terms of service for a user. For a user that has taken +action in this terms of service, this will update their status. If the user has never taken action on this terms of service then this will return a 404 Not Found Error. [terms_of_service_user_status_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.terms_of_service_user_status.TermsOfServiceUserStatus @@ -132,18 +131,18 @@ service then this will return a 404 Not Found Error. [terms_of_service_user_status]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.client.Client.terms_of_service_user_status [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Get User Status for a Terms of Service -------------------------------------- +## Get User Status for a Terms of Service -To get a terms of service user status object, first call -[`client.terms_of_service_user_status(status_id)`][terms_of_service_user_status] -to construct the appropriate [`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object. Then calling -[`client.user(user_id)`][user] to construct the user you wish to retrieve a -[`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object for. Finally, calling -[`terms_of_service_user_status.get(*, fields=None, headers=None, **kwargs)`][get] will return the +To get a terms of service user status object, first call +[`client.terms_of_service_user_status(status_id)`][terms_of_service_user_status] +to construct the appropriate [`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object. Then calling +[`client.user(user_id)`][user] to construct the user you wish to retrieve a +[`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object for. Finally, calling +[`terms_of_service_user_status.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`TermsOfServiceUserStatus`][terms_of_service_user_status_class] object populated with data from the API. + ```python user = client.user(user_id='11111') user_status = client.terms_of_service(tos_id='12345').get_user_status(user) diff --git a/docs/usage/trash.md b/docs/usage/trash.md index 4242cd3e9..0d6d7224f 100644 --- a/docs/usage/trash.md +++ b/docs/usage/trash.md @@ -1,5 +1,4 @@ -Trash -===== +# Trash Under normal circumstances, when an item in Box is deleted, it is not actually erased immediately. Instead, it is moved to the Trash. The Trash allows you to recover files and folders that have been deleted. By default, items in @@ -8,7 +7,6 @@ the Trash will be purged after 30 days. - - [List Trashed Items](#list-trashed-items) - [Get Trashed Items](#get-trashed-items) - [Restore Item from Trash](#restore-item-from-trash) @@ -16,14 +14,14 @@ the Trash will be purged after 30 days. -List Trashed Items ------------------- +## List Trashed Items -To retrieve all trashed items for an enterprise, call [`client.trash().get_items(imit=None, offset=None, fields=None)`][get_trashed_items]. -This method returns a `BoxObjectCollection` that allows you to iterate over the [`Trash`][trash] objects in the +To retrieve all trashed items for an enterprise, call [`client.trash().get_items(imit=None, offset=None, fields=None)`][get_trashed_items]. +This method returns a `BoxObjectCollection` that allows you to iterate over the [`Trash`][trash] objects in the collection. + ```python trashed_items = client.trash().get_items() for trashed_item in trashed_items: @@ -33,13 +31,13 @@ for trashed_item in trashed_items: [get_trashed_item]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.trash.Trash.get_trashed_items [trash]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.trash.Trash -Get Trashed Items ------------------ +## Get Trashed Items -To get a trashed item, call [`client.trash().get_item(item, fields)`][get_item] with the item you wish to retrieve passed in. +To get a trashed item, call [`client.trash().get_item(item, fields)`][get_item] with the item you wish to retrieve passed in. This method will return the ['Item'][item] object populated with data from the API. + ```python file_to_retrieve = client.file(file_id='11111') file_from_trash = client.trash().get_item(file_to_retrieve) @@ -47,6 +45,7 @@ print(f'File ID is {file_from_trash.id} and name is {file_from_trash.name}') ``` + ```python folder = client.folder(folder_id='22222') folder_from_trash = client.trash().get_item(folder) @@ -54,6 +53,7 @@ print(f'Folder ID is {folder_from_trash.id} and name is {folder_from_trash.name} ``` + ```python web_link = client.web_link(web_link_i='33333') web_link_from_trash = client.trash().get_item(web_link) @@ -63,20 +63,22 @@ print(f'Web link ID is {web_link_from_trash.id} and name is {web_link_from_trash [item]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item [get_item]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.trash.Trash.get_item -Restore Item from Trash ------------------------ +## Restore Item from Trash -To retore a trashed item, effectively undeleting it, call [`client.trash().restore(item, name=None, parent_folder, fields=None)`][restore_item] -with the constructed [`Item`][item_class] object will let you restore the specific object from your trash. This method +To retore a trashed item, effectively undeleting it, call [`client.trash().restore(item, name=None, parent_folder, fields=None)`][restore_item] +with the constructed [`Item`][item_class] object will let you restore the specific object from your trash. This method will return a [`Item`][item_class] object populated with data from the API, leaving the original object unmodified. + ```python file_to_restore = client.file(file_id='11111') restored_file = client.trash().restore_item(file_to_restore) print(f'File ID is {restored_file.id} and name is {restored_file.name}') ``` + + ```python folder_to_restore = client.folder(folder_id='22222') restored_folder = client.trash().restore_item(folder_to_restore) @@ -84,6 +86,7 @@ print(f'Folder ID is {restored_folder.id} and name is {restored_folder.name}') ``` + ```python web_link_to_restore = client.web_link(web_link_id='33333') restored_web_link = client.trash().restore_item(web_link_to_restore) @@ -103,13 +106,13 @@ print(f'New name for file is {restored_file.name} and new parent folder is {rest [item_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item [restore_item]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.trash.Trash.restore_item -Permanently Delete Item ------------------------ +## Permanently Delete Item -To delete an [`Item`][item_class] object from trash, call [`client.trash().permanently_delete_item(item)`][delete]. +To delete an [`Item`][item_class] object from trash, call [`client.trash().permanently_delete_item(item)`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python file_to_delete = client.file(file_id='11111') client.trash().permanently_delete_item(file_to_delete) @@ -117,6 +120,7 @@ print('The file was deleted from trash!') ``` + ```python folder = client.folder(folder_id='22222') client.trash().permanently_delete_item(folder) @@ -124,6 +128,7 @@ print('The folder was deleted from trash!') ``` + ```python web_link = client.web_link(web_link_id='33333') client.trash().permanently_delete_item(web_link) diff --git a/docs/usage/user.md b/docs/usage/user.md index 677237238..9f757e10c 100644 --- a/docs/usage/user.md +++ b/docs/usage/user.md @@ -1,5 +1,4 @@ -Users -===== +# Users Users represent an individual's account on Box. @@ -25,13 +24,13 @@ Users represent an individual's account on Box. -Get User Information --------------------- +## Get User Information To get information about a user, call the [`user.get(*, fields=None, headers=None, **kwargs)`][object_get] method. This method returns a new [`User`][user_class] object with fields populated by data from the API. + ```python user_id = '33333' user = client.user(user_id).get() @@ -50,39 +49,39 @@ if user.is_sync_enabled: [object_get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get [user_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User -Get the Current User's Information ----------------------------------- +## Get the Current User's Information To get the current user, call [`client.user(user_id='me')`][user_init] to create the [`User`][user_class] object and then call [`user.get(*, fields=None, headers=None, **kwargs)`][object_get] to retrieve the user information from the API. + ```python current_user = client.user().get() ``` [user_init]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.user -Create An Enterprise User -------------------------- +## Create An Enterprise User To create an enterprise user, call the [`client.create_user(name, login, **user_attributes)`][create_user] method. This method returns a new [`User`][user_class] object. + ```python new_user = client.create_user('Temp User', 'user@example.com') ``` [create_user]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.create_user -Get the Avatar for a User -------------------------- +## Get the Avatar for a User -To get the avatar for a user call the [`user.get_avatar()`][get_avatar] method with the [`User`][user_class] +To get the avatar for a user call the [`user.get_avatar()`][get_avatar] method with the [`User`][user_class] object for the user you wish to retrieve an avatar for. This will return the user avatar to you in bytes. + ```python avatar = client.user('33333').get_avatar() ``` @@ -92,13 +91,15 @@ avatar = client.user('33333').get_avatar() ## Upload user avatar To add or update the avatar for a user, call the [`user.upload_avatar(image_path)`][upload-avatar] method with the [`User`][user_class]. -Put the path to your image as a method parameter. The supported image extensions are `jpg`, `jpeg` and `png`. -The image size cannot exceed 1024 * 1024 pixels or 1MB. +Put the path to your image as a method parameter. The supported image extensions are `jpg`, `jpeg` and `png`. +The image size cannot exceed 1024 \* 1024 pixels or 1MB. + ```python avatar_urls = client.user('33333').upload_avatar(image_path='path/to/the/image.png') ``` + In return, you will get links to several representations of an avatar within Box account. Alternatively you can upload the avatar by passing the image byte stream and the image extension to @@ -112,39 +113,39 @@ avatar_urls = client.user('33333').upload_avatar_stream(image_stream=image_strea [upload-avatar]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.user.User.upload_avatar [upload-avatar-stream]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.user.User.upload_avatar_stream - ## Delete user avatar To delete the user avatar image use [`delete_avatar()`][delete-avatar] method with the [`User`][user_class]. + ```java client.user('33333').delete_avatar() ``` [delete-avatar]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.user.User.delete_avatar -Create An App User ------------------- +## Create An App User Platform applications may create App Users, which represent a "headless" user managed exclusively by the application. These users can only be accessed via the API, and cannot login to the web application or other Box services. To create a new app user, call [`client.create_user(name, login=None, **user_attributes)`][create_user] without a -`login` value. This returns the [`User`][user_class] object for the new app user. +`login` value. This returns the [`User`][user_class] object for the new app user. + ```python new_app_user = client.create_user('App User 123', login=None) ``` -Update User ------------ +## Update User To update a user object, call the [`user.update_info(data=data_to_update)`][update_info] method with a `dict` of fields to update on the user. + ```python user_id = '33333' user = client.user(user_id) @@ -153,17 +154,17 @@ updated_user = user.update_info(data={'name': 'Smart User'}) [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Delete User ------------ +## Delete User -To delete a user call the [`user.delete(notify=True, force=False)`][delete] method. The method returns `True` to +To delete a user call the [`user.delete(notify=True, force=False)`][delete] method. The method returns `True` to indicate that the deletion succeeded. The `notify` parameter determines whether the user should receive an email about the deletion, -and the `force` parameter will cause the user to be deleted even if they still have files in their account. If `force` +and the `force` parameter will cause the user to be deleted even if they still have files in their account. If `force` is set to `False` and the user still has files in their account, the deletion will fail. + ```python user_id = '33333' client.user(user_id).delete(force=True) @@ -171,13 +172,13 @@ client.user(user_id).delete(force=True) [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User.delete -Invite User to Enterprise -------------------------- +## Invite User to Enterprise -To invite an existing user to join an Enterprise call the [`enterprise.invite_user(user_email)`][invite_user] method. This +To invite an existing user to join an Enterprise call the [`enterprise.invite_user(user_email)`][invite_user] method. This method returns an [`Invite`][invite_class] object representing the status of the invitation. + ```python enterprise = client.get_current_enterprise() invitation = enterprise.invite_user('user@example.com') @@ -186,14 +187,14 @@ invitation = enterprise.invite_user('user@example.com') [invite_user]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.enterprise.Enterprise.invite_user [invite_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.invite.Invite -Get Email Aliases ------------------ +## Get Email Aliases To get a user's email aliases call the [`user.get_email_aliases(limit=None, fields=None)`][get_email_aliases] method. This method returns a [`BoxObjectCollection`][box_object_collection] used to iterate over the collection of [`EmailAlias`][email_alias_class] objects. + ```python user_id = '33333' user = client.user(user_id) @@ -206,14 +207,14 @@ for alias in email_aliases: [box_object_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.pagination.box_object_collection.BoxObjectCollection [email_alias_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.email_alias.EmailAlias -Add Email Alias ---------------- +## Add Email Alias To add an email alias for a user, call the [`user.add_email_alias(email)`][add_email_alias] method with the email -address to add as an email alias for the user. This will allow the user to log in and be collaborated by this email -in addition to their login email address. Not all emails addresses can be added as email aliases. Email addresses whose domains match the domain of the login email address can always be made aliases. Email addresses whose domains differ from the domain of the login email address can be made aliases depending on the Box account configuration. The method returns an [`EmailAlias`][email_alias_class] object. +address to add as an email alias for the user. This will allow the user to log in and be collaborated by this email +in addition to their login email address. Not all emails addresses can be added as email aliases. Email addresses whose domains match the domain of the login email address can always be made aliases. Email addresses whose domains differ from the domain of the login email address can be made aliases depending on the Box account configuration. The method returns an [`EmailAlias`][email_alias_class] object. + ```python user_id = '33333' user = client.user(user_id) @@ -222,13 +223,13 @@ email_alias = user.add_email_alias('alias@example.com') [add_email_alias]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User.add_email_alias -Remove Email Alias ------------------- +## Remove Email Alias To remove an email alias from a user, call the [`user.remove_email_alias(email_alias)`][remove_email_alias] method with -the [`EmailAlias`][email_alias_class] object to remove. The method returns `True` to signify that the removal succeeded. +the [`EmailAlias`][email_alias_class] object to remove. The method returns `True` to signify that the removal succeeded. + ```python user_id = '33333' email_alias_id = '12345' @@ -241,16 +242,16 @@ user.remove_email_alias(email_alias) [remove_email_alias]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.user.User.remove_email_alias -Get Enterprise Users --------------------- +## Get Enterprise Users To get the users in an enterprise, call -[`client.users(limit=None, offset=0, filter_term=None, user_type=None, fields=None)`][get_users]. You can specify +[`client.users(limit=None, offset=0, filter_term=None, user_type=None, fields=None)`][get_users]. You can specify a `filter_term` to filter on the user's `name` and `login` fields, or select a `user_type` to filter down to only -managed or external users. This method returns a [`BoxObjectCollection`][box_object_collection] used to iterate over +managed or external users. This method returns a [`BoxObjectCollection`][box_object_collection] used to iterate over the collection of [`User`][user_class] objects. + ```python users = client.users(user_type='all') for user in users: @@ -259,16 +260,16 @@ for user in users: [get_users]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.users -Transfer User Content ---------------------- +## Transfer User Content To move all of a user's content to a different user, call the [`user.transfer_content(self, destination_user, notify=None, fields=None)`][transfer_content] method with the -[`User`][user_class] object representing the destination user. This will create a new folder in the destination user's +[`User`][user_class] object representing the destination user. This will create a new folder in the destination user's account, containing all files and folders from the original user's account; the method returns a [`Folder`][folder_class] object representing this new folder in the destination user's account. + ```python source_user_id = '33333' destination_user_id = '44444' diff --git a/docs/usage/watermarking.md b/docs/usage/watermarking.md index 2e9d2f4b3..0737d7164 100644 --- a/docs/usage/watermarking.md +++ b/docs/usage/watermarking.md @@ -1,16 +1,14 @@ -Watermarking -============ +# Watermarking -The ability to watermark files and folders is represented as a sub-resource on the Files and Folders resources, -respectively. You can think of the sub-resource as a "label" marking whether the file or folder is watermarked or not. -If you apply a watermark label to a folder, then all files inside of it will be protected by the watermark (e.g. -previews will be watermarked). However, those files' watermark sub-resource is independent from the folder that got +The ability to watermark files and folders is represented as a sub-resource on the Files and Folders resources, +respectively. You can think of the sub-resource as a "label" marking whether the file or folder is watermarked or not. +If you apply a watermark label to a folder, then all files inside of it will be protected by the watermark (e.g. +previews will be watermarked). However, those files' watermark sub-resource is independent from the folder that got watermarked. This allows you to watermark files and folders independently. - - [Watermarking](#watermarking) - [Get Watermark on File or Folder](#get-watermark-on-file-or-folder) - [Apply Watermark on File or Folder](#apply-watermark-on-file-or-folder) @@ -18,20 +16,21 @@ watermarked. This allows you to watermark files and folders independently. -Get Watermark on File or Folder -------------------------------- +## Get Watermark on File or Folder -To get a watermark object, call [`file.get_watermark()`][get_file_watermark] or -[`folder.get_watermark()`][get_folder_watermark] will return the [`Watermark`][watermark_class] object populated with +To get a watermark object, call [`file.get_watermark()`][get_file_watermark] or +[`folder.get_watermark()`][get_folder_watermark] will return the [`Watermark`][watermark_class] object populated with data from the API. + ```python watermark = client.file(file_id='12345').get_watermark() print(f'Watermark created at {watermark.created_at} and modified at {watermark.modified_at}') ``` + ```python watermark = client.folder(folder_id='11111').get_watermark() print(f'Watermark created at {watermark.created_at} and modified at {watermark.modified_at}') @@ -41,20 +40,21 @@ print(f'Watermark created at {watermark.created_at} and modified at {watermark.m [get_folder_watermark]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.get_watermark() [watermark_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.watermark.Watermark -Apply Watermark on File or Folder ---------------------------------- +## Apply Watermark on File or Folder -To assign a watermark on a file or folder, call [file.apply_watermark()][apply-file-watermark] or -[folder.apply_watermark()][apply-folder-watermark] will return the [`Watermark`][watermark_class] object populated with +To assign a watermark on a file or folder, call [file.apply_watermark()][apply-file-watermark] or +[folder.apply_watermark()][apply-folder-watermark] will return the [`Watermark`][watermark_class] object populated with data from the API. + ```python watermark = client.file(file_id='12345').apply_watermark() print(f'Watermark created at {watermark.created_at} and modified at {watermark.modified_at}') ``` + ```python watermark = client.folder(folder_id='11111').apply_watermark() print(f'Watermark created at {watermark.created_at} and modified at {watermark.modified_at}') @@ -64,19 +64,20 @@ print(f'Watermark created at {watermark.created_at} and modified at {watermark.m [apply_folder_watermark]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.folder.Folder.apply_watermark() [watermark_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.watermark.Watermark -Remove Watermark on File or Folder ----------------------------------- +## Remove Watermark on File or Folder -To remove a watermark from a file or folder, call [file.delete_watermark()][delete-file-watermark] or +To remove a watermark from a file or folder, call [file.delete_watermark()][delete-file-watermark] or [folder.delete_watermark()][delete-folder-watermark] will return `True` to indicate that the deletion was successful. + ```python client.file(file_id='12345').delete_watermark() print('The file watermark was deleted!') ``` + ```python client.folder(folder_id='11111').delete_watermark() print('The folder watermark was deleted!') diff --git a/docs/usage/web_link.md b/docs/usage/web_link.md index 518ca774d..92e94950d 100644 --- a/docs/usage/web_link.md +++ b/docs/usage/web_link.md @@ -1,5 +1,4 @@ -Web Links -========= +# Web Links Web links are objects that point to URLs. These objects are also known as bookmarks within the Box web application. Web link objects are treated @@ -7,7 +6,8 @@ similarly to file objects. -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +**Table of Contents** _generated with [DocToc](https://github.com/thlorenz/doctoc)_ - [Create Web Link](#create-web-link) - [Get Web Link](#get-web-link) @@ -23,14 +23,14 @@ similarly to file objects. -Create Web Link ---------------- +## Create Web Link -To create a web link object, calling [`folder.create_web_link(target_url, name=None, description=None)`][create] -will let you create a new web link with a specified name and description. This method return an newly created [`WebLink`][web_link_class] +To create a web link object, calling [`folder.create_web_link(target_url, name=None, description=None)`][create] +will let you create a new web link with a specified name and description. This method return an newly created [`WebLink`][web_link_class] object populated with data from the API, leaving the original object unmodified. + ```python web_link = client.folder(folder_id='12345').create_web_link('https://example.com', 'Example Link', 'This is the description') print(f'Web Link url is {web_link.url} and its description is {web_link.description}') @@ -39,14 +39,14 @@ print(f'Web Link url is {web_link.url} and its description is {web_link.descript [create]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.folder.Folder.create_web_link [web_link_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink -Get Web Link ------------- +## Get Web Link -To get a web link object, first call [`client.web_link(web_link_id)`][web_link] to construct the appropriate +To get a web link object, first call [`client.web_link(web_link_id)`][web_link] to construct the appropriate [`WebLink`][web_link_class] object, and then calling [`web_link.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`WebLink`][web_link_class] object populated with data from the API, leaving the original object unmodified. + ```python web_link = client.web_link(web_link_id='12345').get() print(f'Web Link ID is {web_link.id} and its type is {web_link.type}') @@ -56,11 +56,10 @@ print(f'Web Link ID is {web_link.id} and its type is {web_link.type}') [web_link_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -Update Web Link ---------------- +## Update Web Link -To update a web link object, call [`web_link.update_info(data=data_to_update)`][update_info] with a `dict` of -properties to update on the web link. This method returns a newly updated ['WebLink'][web_link_class] object, leaving +To update a web link object, call [`web_link.update_info(data=data_to_update)`][update_info] with a `dict` of +properties to update on the web link. This method returns a newly updated ['WebLink'][web_link_class] object, leaving the original object unmodified. ```python @@ -70,11 +69,10 @@ updated_web_link = client.web_link(web_link_id='12345').update_info(data={'url': [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info [web_link_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink -Move a Web Link ------------ +## Move a Web Link To move a web link from one folder into another, call [`web_link.move(parent_folder, name=None)`][move] with the destination -folder to move the web link into. You can optionally provide a `name` parameter to automatically rename the web link +folder to move the web link into. You can optionally provide a `name` parameter to automatically rename the web link in case of a name conflict in the destination folder. This method returns the updated [`WebLink`][web_link_class] object in the new folder. @@ -91,15 +89,15 @@ print(f'Web link "{moved_web_link.name}" has been moved into folder "{moved_web_ [move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.move -Copy a Web Link ------------ +## Copy a Web Link -A web link can be copied to a new folder by calling [`web_link.copy(*, parent_folder, name=None, **_kwargs)`][copy] +A web link can be copied to a new folder by calling [`web_link.copy(*, parent_folder, name=None, **_kwargs)`][copy] with the destination folder and an optional new name for the web link in case there is a name conflict in the -destination folder. This method returns a [`WebLink`][web_link_class] object representing the copy of the web link +destination folder. This method returns a [`WebLink`][web_link_class] object representing the copy of the web link in the destination folder. + ```python web_link = client.web_link(web_link_id='12345') @@ -109,8 +107,7 @@ print(f'Web link "{web_link_copy.name}" has been copied into folder "{web_link_c [copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.copy -Rename a Web Link ------------ +## Rename a Web Link A web link can be renamed by calling [`web_link.rename(name)`][rename]. This method returns the updated [`WebLink`][web_link_class] object with a new name. @@ -124,14 +121,13 @@ print(f'Web link was renamed to "{renamed_web_link.name}"') [rename]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.rename +## Delete Web Link -Delete Web Link ---------------- - -To delete a web link, call [`web_link.delete()`][delete]. This method returns `True` to indicate that the deletion was +To delete a web link, call [`web_link.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python client.web_link('12345').delete() print('The web link was deleted!') @@ -139,14 +135,14 @@ print('The web link was deleted!') [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete -Create a Shared Link --------------------- +## Create a Shared Link A shared link for a web link can be generated by calling [`web_link.get_shared_link(*, access=None, unshared_at=SDK_VALUE_NOT_SET, password=None, vanity_name=None, **kwargs)`][get_shared_link]. This method returns a `unicode` string containing the shared link URL. + ```python url = client.web_link('12345').get_shared_link(access='open') print(f'The web link shared link URL is: {url}') @@ -154,8 +150,7 @@ print(f'The web link shared link URL is: {url}') [get_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.get_shared_link -Update a Shared Link --------------------- +## Update a Shared Link A shared link for a web link can be updated by calling [`web_link.get_shared_link(*, access=None, unshared_at=SDK_VALUE_NOT_SET, password=None, @@ -164,35 +159,34 @@ vanity_name=None, **kwargs)`][update_shared_link] with an updated list of proper This method returns a `unicode` string containing the shared link URL. + ```python url = client.web_link('12345').get_shared_link(access='open', password='letmein') print(f'The web link shared link URL is: {url}') ``` -[update_shared_link]: -https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.get_shared_link +[update_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.get_shared_link -Get a Shared Link --------------------- +## Get a Shared Link To check for an existing shared link on a web link, simply call `web_link.shared_link` This method returns a `unicode` string containing the shared link URL. + ```python shared_link = client.web_link('12345').get().shared_link url = shared_link['url'] ``` -Remove a Shared Link --------------------- +## Remove a Shared Link A shared link for a web link can be removed by calling [`web_link.remove_shared_link(**kwargs)`][remove_shared_link]. + ```python client.web_link('12345').remove_shared_link() ``` -[remove_shared_link]: -https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.remove_shared_link +[remove_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.remove_shared_link diff --git a/docs/usage/webhook.md b/docs/usage/webhook.md index 59d2236fd..ba54b6adf 100644 --- a/docs/usage/webhook.md +++ b/docs/usage/webhook.md @@ -1,14 +1,12 @@ -Webhooks -======== +# Webhooks -Webhooks enable you to attach event triggers to Box files and folders. Event triggers monitor events on Box objects and -notify your application when they occur. A webhook notifies your application by sending HTTP requests to a URL of your +Webhooks enable you to attach event triggers to Box files and folders. Event triggers monitor events on Box objects and +notify your application when they occur. A webhook notifies your application by sending HTTP requests to a URL of your choosing. - - [Get Information about Webhook](#get-information-about-webhook) - [List all Webhooks](#list-all-webhooks) - [Create Webhook](#create-webhook) @@ -18,14 +16,14 @@ choosing. -Get Information about Webhook ------------------------------ +## Get Information about Webhook -To get a webhook object, first call [`client.webhook(webhook_id)`][webhook] to construct the appropriate +To get a webhook object, first call [`client.webhook(webhook_id)`][webhook] to construct the appropriate [`Webhook`][webhook_class] object, and then calling [`webhook.get(*, fields=None, headers=None, **kwargs)`][get] will return the [`Webhook`][webhook_class] object populated with data from the API. + ```python webhook = client.webhook(webhook_id='12345').get() print(f'Webhooks ID is {webhook.id} and the address is {webhook.address}') @@ -35,14 +33,14 @@ print(f'Webhooks ID is {webhook.id} and the address is {webhook.address}') [webhook_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.webhook.Webhook [get]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.get -List all Webhooks ------------------ +## List all Webhooks -To retrieve all webhooks for an enterprise, call [`client.get_webhooks(limit=None, marker=None, fields=None)`][get_webhooks]. -This method returns a `BoxObjectCollection` that allows you to iterate over the [`Webhook`][webhook_class] objects in +To retrieve all webhooks for an enterprise, call [`client.get_webhooks(limit=None, marker=None, fields=None)`][get_webhooks]. +This method returns a `BoxObjectCollection` that allows you to iterate over the [`Webhook`][webhook_class] objects in the collection. + ```python webhooks = client.get_webhooks() for webhook in webhooks: @@ -52,14 +50,14 @@ for webhook in webhooks: [get_webhooks]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.get_webhooks [webhook_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.webhook.Webhook -Create Webhook --------------- +## Create Webhook -To create a webhook object, call [`client.create_webhook(target_url, name=None, description=None)`][create] will let -you create a new webhook object with the specified target url, name, and description. This method will return an updated +To create a webhook object, call [`client.create_webhook(target_url, name=None, description=None)`][create] will let +you create a new webhook object with the specified target url, name, and description. This method will return an updated [`Webhook`][webhook_class] object populated with data from the API, leaving the original object unmodified. + ```python file = client.file(file_id='12345') webhook = client.create_webhook(file, ['FILE.PREVIEWED'], 'https://example.com') @@ -67,6 +65,7 @@ print(f'Webhook ID is {webhook.id} and the address is {webhook.address}') ``` + ```python folder = client.folder(folder_id='12345') webhook = client.create_webhook(folder, ['FILE.UPLOADED', 'FILE.PREVIEWED'], 'https://example.com') @@ -76,13 +75,13 @@ print(f'Webhook ID is {webhook.id} and the address is {webhook.address}') [create]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.client.html#boxsdk.client.client.Client.create_webhook [webhook_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.webhook.Webhook -Delete Webhook --------------- +## Delete Webhook -To delete a webhook, call [`webhook.delete()`][delete]. This method returns `True` to indicate that the deletion was +To delete a webhook, call [`webhook.delete()`][delete]. This method returns `True` to indicate that the deletion was successful. + ```python client.webhook(webhook_id='12345').delete() print('The webhook was successfully deleted!') @@ -90,15 +89,14 @@ print('The webhook was successfully deleted!') [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete +## Update Webhook -Update Webhook --------------- - -To update a webhook object, first call [`client.webhook(webhook_id)`][webhook] to construct the appropriate [`Webhook`][webhook_class] -object, and then calling [`webhook.update_info(data=update_object)`][update_info] with a `dict` of properties to update on the +To update a webhook object, first call [`client.webhook(webhook_id)`][webhook] to construct the appropriate [`Webhook`][webhook_class] +object, and then calling [`webhook.update_info(data=update_object)`][update_info] with a `dict` of properties to update on the webhook. This method returns a new updated [`Webhook`][webhook_class] object, leaving the original object unmodified. + ```python update_object = { 'triggers': ['FILE.COPIED'], @@ -112,15 +110,15 @@ print(f'Updated the webhook info for triggers: {webhook.triggers} and address: { [webhook_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.webhook.Webhook [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info -Validate Webhook Message ------------------------- +## Validate Webhook Message -When you receive a webhook message from Box, you should validate it by calling +When you receive a webhook message from Box, you should validate it by calling [`Webhook.validate_message(body, headers, primary_key, secondary_key)`][validate_webhook]. This will protect your application against attacks. Verification ensures that the notifications were actually sent by Box and not by a malicious party and that the contents of the notification haven't been changed. + ```python body = b'{"webhook":{"id":"1234567890"},"trigger":"FILE.UPLOADED","source":{"id":"1234567890","type":"file","name":"Test.txt"}}' headers = { diff --git a/docs/usage/zip.md b/docs/usage/zip.md index 9ca3cf435..6daf3d077 100644 --- a/docs/usage/zip.md +++ b/docs/usage/zip.md @@ -1,5 +1,4 @@ -Zip -======== +# Zip Allows you to create a temporary zip file on Box, containing Box files and folders, and then download it. @@ -10,13 +9,13 @@ Allows you to create a temporary zip file on Box, containing Box files and folde -Download a Zip File ------------------------------ +## Download a Zip File -Calling [`client.download_zip(name, items, writable_stream)`][create_zip] will let you create a new zip file +Calling [`client.download_zip(name, items, writable_stream)`][create_zip] will let you create a new zip file with the specified name and with the specified items and download it to the stream that is passed in. The response is a status `dict` that contains information about the download, including whether it was successful. The created zip file does not show up in your Box account. + ```python name = 'test' file = mock_client.file('466239504569') diff --git a/setup.py b/setup.py index 8c8ac6618..913ae9ac7 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from codecs import open # pylint:disable=redefined-builtin +from codecs import open # pylint:disable=redefined-builtin from os.path import dirname, join import re import sys @@ -6,7 +6,6 @@ from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand - CLASSIFIERS = [ 'Development Status :: 6 - Mature', 'Intended Audience :: Developers', @@ -47,6 +46,7 @@ def run_tests(self): # Do the import here, once the eggs are loaded. # pylint:disable=import-outside-toplevel import pytest + errno = pytest.main(self.pytest_args) sys.exit(errno) @@ -75,7 +75,7 @@ def main(): 'pytest-cov<5.0.0', 'pytest-lazy-fixture<1.0.0', 'pytz', - 'urllib3<2' + 'urllib3<2', ] extra_requires = { 'jwt': jwt_requires, @@ -86,20 +86,26 @@ def main(): 'test': test_requires, } with open('boxsdk/version.py', encoding='utf-8') as config_py: - version = re.search(r'^\s*__version__\s*=\s*[\'"]([^\'"]*)[\'"]', config_py.read(), re.MULTILINE).group(1) + version = re.search( + r'^\s*__version__\s*=\s*[\'"]([^\'"]*)[\'"]', config_py.read(), re.MULTILINE + ).group(1) setup( name='boxsdk', version=version, description='Official Box Python SDK', long_description_content_type="text/markdown", - long_description=open(join(base_dir, 'README.md'), encoding='utf-8').read(), # pylint:disable=consider-using-with + long_description=open( + join(base_dir, 'README.md'), encoding='utf-8' + ).read(), # pylint:disable=consider-using-with author='Box', author_email='oss@box.com', url='https://github.com/box/box-python-sdk', project_urls={ 'Changelog': 'https://github.com/box/box-python-sdk/blob/main/CHANGELOG.md', }, - packages=find_packages(exclude=['demo', 'docs', 'test', 'test*', '*test', '*test*']), + packages=find_packages( + exclude=['demo', 'docs', 'test', 'test*', '*test', '*test*'] + ), install_requires=install_requires, extras_require=extra_requires, tests_require=test_requires, diff --git a/test/conftest.py b/test/conftest.py index d3dd33d2b..494c14a47 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -24,7 +24,9 @@ def _set_content_and_json_from_json(mock_response, json_value): def _set_content_and_json_from_content(mock_response, content): if not isinstance(content, bytes): - raise TypeError(f"Expected 'content' to be byte string, got {content.__class__.__name__!r}.") + raise TypeError( + f"Expected 'content' to be byte string, got {content.__class__.__name__!r}." + ) mock_response.content = content mock_response.headers['Content-Length'] = str(len(content)) try: @@ -36,7 +38,9 @@ def _set_content_and_json_from_content(mock_response, content): @pytest.fixture() def generic_successful_request_response(): mock_request_response = Mock(headers={f'header{i}': f'value{i}' for i in range(4)}) - _set_content_and_json_from_json(mock_request_response, json_value={f'key{i}': f'value{i}' for i in range(8)}) + _set_content_and_json_from_json( + mock_request_response, json_value={f'key{i}': f'value{i}' for i in range(8)} + ) mock_request_response.status_code = 200 mock_request_response.ok = True mock_request_response.request = Mock() @@ -56,7 +60,9 @@ def _network_response_mock_from_request_response(request_response): @pytest.fixture() def generic_successful_response(generic_successful_request_response): - return _network_response_mock_from_request_response(generic_successful_request_response) + return _network_response_mock_from_request_response( + generic_successful_request_response + ) @pytest.fixture(scope='session') @@ -75,7 +81,9 @@ def successful_token_json_response(access_token, refresh_token): def successful_token_request_response(successful_token_json_response): # pylint:disable=redefined-outer-name successful_token_mock = Mock(headers={}) - _set_content_and_json_from_json(successful_token_mock, json_value=successful_token_json_response) + _set_content_and_json_from_json( + successful_token_mock, json_value=successful_token_json_response + ) successful_token_mock.ok = True successful_token_mock.status_code = 200 return successful_token_mock @@ -83,7 +91,9 @@ def successful_token_request_response(successful_token_json_response): @pytest.fixture() def successful_token_response(successful_token_request_response): - return _network_response_mock_from_request_response(successful_token_request_response) + return _network_response_mock_from_request_response( + successful_token_request_response + ) @pytest.fixture() @@ -139,7 +149,9 @@ def retry_after_response(retry_after_response_202, retry_after_response_429, req def _server_error_request_response(status_code): mock_request_response = Mock(headers={f'header{i}': f'value{i}' for i in range(4)}) - _set_content_and_json_from_json(mock_request_response, json_value={f'key{i}': f'value{i}' for i in range(8)}) + _set_content_and_json_from_json( + mock_request_response, json_value={f'key{i}': f'value{i}' for i in range(8)} + ) mock_request_response.status_code = status_code mock_request_response.ok = False return mock_request_response @@ -156,7 +168,9 @@ def server_error_request_response_503(): @pytest.fixture(params=[502, 503]) -def server_error_request_response(server_error_request_response_502, server_error_request_response_503, request): +def server_error_request_response( + server_error_request_response_502, server_error_request_response_503, request +): if request.param == 502: return server_error_request_response_502 if request.param == 503: @@ -173,7 +187,9 @@ def server_error_response(server_error_request_response): def bad_network_response(): mock_network_response = Mock(DefaultNetworkResponse, headers={}) mock_network_response.status_code = 404 - _set_content_and_json_from_json(mock_network_response, json_value={'code': 404, 'message': 'Not Found'}) + _set_content_and_json_from_json( + mock_network_response, json_value={'code': 404, 'message': 'Not Found'} + ) mock_network_response.ok = False return mock_network_response @@ -182,7 +198,13 @@ def bad_network_response(): def bad_network_response_400(): mock_network_response = Mock(DefaultNetworkResponse, headers={}) mock_network_response.status_code = 400 - _set_content_and_json_from_json(mock_network_response, json_value={'error': 'Example Error', 'error_description': 'Example Error Description'}) + _set_content_and_json_from_json( + mock_network_response, + json_value={ + 'error': 'Example Error', + 'error_description': 'Example Error Description', + }, + ) mock_network_response.ok = False return mock_network_response @@ -232,11 +254,13 @@ def auth_code(): return 'fake_auth_code' -@pytest.fixture(params=[ - b'Hello', - b'Goodbye', - b'42', -]) +@pytest.fixture( + params=[ + b'Hello', + b'Goodbye', + b'42', + ] +) def test_file_content(request): return request.param @@ -279,7 +303,9 @@ def mock_datetime_rfc3339_str(): @pytest.fixture(scope='module') def mock_timezone_aware_datetime_obj(): - return datetime.datetime(2035, 3, 4, 10, 14, 24, microsecond=500, tzinfo=pytz.timezone('US/Alaska')) + return datetime.datetime( + 2035, 3, 4, 10, 14, 24, microsecond=500, tzinfo=pytz.timezone('US/Alaska') + ) @pytest.fixture(scope='module') diff --git a/test/functional/conftest.py b/test/functional/conftest.py index ed3ef78d0..faaf52e1e 100644 --- a/test/functional/conftest.py +++ b/test/functional/conftest.py @@ -39,15 +39,24 @@ def box_session(box_oauth, network_layer): @pytest.fixture() def box_oauth(client_id, client_secret, user_login, unauthorized_session): # pylint:disable=redefined-outer-name - oauth2 = OAuth2(client_id, client_secret, box_device_name='mock_box functional test', session=unauthorized_session) + oauth2 = OAuth2( + client_id, + client_secret, + box_device_name='mock_box functional test', + session=unauthorized_session, + ) url, _ = oauth2.get_authorization_url('http://localhost') form = requests.get(url + '&box_login=' + user_login).content.decode('utf-8') form_action = re.search('action="([^"]*)"', form).group(1) - auth_response = requests.post(form_action, allow_redirects=False, data={ - 'login': user_login, - 'client_id': client_id, - 'client_secret': client_secret, - }) + auth_response = requests.post( + form_action, + allow_redirects=False, + data={ + 'login': user_login, + 'client_id': client_id, + 'client_secret': client_secret, + }, + ) redirect_url = auth_response.headers['Location'] query_string = parse.urlparse(redirect_url).query parsed_query_string_dict = parse.parse_qs(query_string) @@ -72,29 +81,39 @@ def mock_box_server(request): @pytest.fixture(autouse=True) -def mock_box(mock_box_server, monkeypatch, client_id, client_secret, user_name, user_login): +def mock_box( + mock_box_server, monkeypatch, client_id, client_secret, user_name, user_login +): # pylint:disable=redefined-outer-name - mock_box_server.reset_filesystem([(user_name, user_login)], [(client_id, client_secret, 0)]) + mock_box_server.reset_filesystem( + [(user_name, user_login)], [(client_id, client_secret, 0)] + ) monkeypatch.setattr(API, 'BASE_API_URL', f'http://localhost:{Box.API_PORT}') monkeypatch.setattr(API, 'UPLOAD_URL', f'http://localhost:{Box.UPLOAD_PORT}') monkeypatch.setattr(API, 'OAUTH2_API_URL', f'http://localhost:{Box.OAUTH_API_PORT}') - monkeypatch.setattr(API, 'OAUTH2_AUTHORIZE_URL', f'http://localhost:{Box.OAUTH_AUTHORIZE_PORT}') + monkeypatch.setattr( + API, 'OAUTH2_AUTHORIZE_URL', f'http://localhost:{Box.OAUTH_AUTHORIZE_PORT}' + ) return mock_box_server -@pytest.fixture(params=[ - 'foo.txt', - 'bar.docx', - 'foo.txt', -]) +@pytest.fixture( + params=[ + 'foo.txt', + 'bar.docx', + 'foo.txt', + ] +) def file_name(request): return request.param -@pytest.fixture(params=[ - 'some_folder', - 'Ѵȁćȁƭȉőń Ρȉćƭȕŕȅŝ', -]) +@pytest.fixture( + params=[ + 'some_folder', + 'Ѵȁćȁƭȉőń Ρȉćƭȕŕȅŝ', + ] +) def folder_name(request): return request.param @@ -112,7 +131,11 @@ def user_login(): @pytest.fixture() def uploaded_file(box_client, test_file_path, test_file_content, file_name): # pylint:disable=redefined-outer-name - with patch('boxsdk.object.folder.open', streamable_mock_open(read_data=test_file_content), create=True): + with patch( + 'boxsdk.object.folder.open', + streamable_mock_open(read_data=test_file_content), + create=True, + ): return box_client.folder('0').upload(test_file_path, file_name) diff --git a/test/functional/mock_box/behavior/event_behavior.py b/test/functional/mock_box/behavior/event_behavior.py index fcf7a1ce4..fc2c24b7e 100644 --- a/test/functional/mock_box/behavior/event_behavior.py +++ b/test/functional/mock_box/behavior/event_behavior.py @@ -23,7 +23,9 @@ def handle_event(*_): def _ms_since_epoch(self, when): delta = when - self._epoch - total_seconds = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6) / 10 ** 6 + total_seconds = ( + delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10**6 + ) / 10**6 return int(total_seconds * 1000) def _ms_after_epoch(self, when): @@ -33,66 +35,94 @@ def get_events(self): stream_position = request.params.get('stream_position', '0') limit = int(request.params.get('limit', '100')) if stream_position == 'now': - return json.dumps({ - 'total_count': 0, - 'next_stream_position': self._ms_since_epoch(datetime.utcnow()), - 'entries': [], - }) + return json.dumps( + { + 'total_count': 0, + 'next_stream_position': self._ms_since_epoch(datetime.utcnow()), + 'entries': [], + } + ) stream_position = int(stream_position) event_query = self._db_session.query(EventModel).filter( EventModel.stream_position >= self._ms_after_epoch(stream_position or 1), ) total_count = event_query.count() results = event_query.order_by(EventModel.sequence_id).limit(limit).all() - max_stream_position = max(e.stream_position for e in results) if results else self._epoch - next_stream_position = datetime.utcnow() if total_count < limit else max_stream_position - timedelta(seconds=5) + max_stream_position = ( + max(e.stream_position for e in results) if results else self._epoch + ) + next_stream_position = ( + datetime.utcnow() + if total_count < limit + else max_stream_position - timedelta(seconds=5) + ) file_ids = [e.source_id for e in results if e.source_type == 'file'] folder_ids = [e.source_id for e in results if e.source_type == 'folder'] - file_records = self._db_session.query(FileModel).filter(FileModel.file_id.in_(file_ids)).all() if file_ids else [] - folder_records = self._db_session.query(FolderModel).filter(FolderModel.folder_id.in_(folder_ids)).all() if folder_ids else [] + file_records = ( + self._db_session.query(FileModel) + .filter(FileModel.file_id.in_(file_ids)) + .all() + if file_ids + else [] + ) + folder_records = ( + self._db_session.query(FolderModel) + .filter(FolderModel.folder_id.in_(folder_ids)) + .all() + if folder_ids + else [] + ) if results: - results = [{ - 'type': 'event', - 'event_id': e.event_id, - 'event_type': e.event_type, - 'source_id': e.source_id, - 'source': next( - (f for f in file_records if f.file_id == e.source_id), - None - ) or next((f for f in folder_records if f.folder_id == e.source_id), None), - } for e in results] - return json.dumps({ - 'chunk_size': total_count, - 'next_stream_position': self._ms_since_epoch(next_stream_position), - 'entries': results, - }) + results = [ + { + 'type': 'event', + 'event_id': e.event_id, + 'event_type': e.event_type, + 'source_id': e.source_id, + 'source': ( + next( + (f for f in file_records if f.file_id == e.source_id), None + ) + or next( + (f for f in folder_records if f.folder_id == e.source_id), + None, + ) + ), + } + for e in results + ] + return json.dumps( + { + 'chunk_size': total_count, + 'next_stream_position': self._ms_since_epoch(next_stream_position), + 'entries': results, + } + ) @staticmethod def get_long_poll_url(): return { 'chunk_size': 1, - 'entries': [{ - 'type': 'realtime_server', - 'url': 'http://localhost:18089/subscribe?channel=cc807c9c4869ffb1c81a&stream_type=all', - 'ttl': 10, - 'max_retries': 10, - 'retry_timeout': 610, - }] + 'entries': [ + { + 'type': 'realtime_server', + 'url': ( + 'http://localhost:18089/subscribe?channel=cc807c9c4869ffb1c81a&stream_type=all' + ), + 'ttl': 10, + 'max_retries': 10, + 'retry_timeout': 610, + } + ], } def subscribe(self): if self._subscribe_event.isSet(): self._subscribe_event.clear() - return { - 'message': 'new_change' - } + return {'message': 'new_change'} got_event = self._subscribe_event.wait(timeout=600) if got_event: self._subscribe_event.clear() - return { - 'message': 'new_change' - } + return {'message': 'new_change'} else: - return { - 'message': 'reconnect' - } + return {'message': 'reconnect'} diff --git a/test/functional/mock_box/behavior/file_behavior.py b/test/functional/mock_box/behavior/file_behavior.py index 93e06d837..2e4577ac7 100644 --- a/test/functional/mock_box/behavior/file_behavior.py +++ b/test/functional/mock_box/behavior/file_behavior.py @@ -6,7 +6,11 @@ from test.functional.mock_box.behavior.item_behavior import ItemBehavior from test.functional.mock_box.db_model.event_model import EventModel from test.functional.mock_box.db_model.file_model import FileModel -from test.functional.mock_box.util.db_utils import get_file_by_id, get_folder_by_id, get_user_from_header +from test.functional.mock_box.util.db_utils import ( + get_file_by_id, + get_folder_by_id, + get_user_from_header, +) from test.functional.mock_box.util.http_utils import abort from test.functional.mock_box.util import json_utils as json @@ -31,7 +35,13 @@ def update_file(self, file_id): file_object.content = content file_object.sha1 = file_hash.hexdigest() file_object.size = len(content) - self._db_session.add(EventModel(event_type='ITEM_UPLOAD', source_id=file_object.file_id, source_type='file')) + self._db_session.add( + EventModel( + event_type='ITEM_UPLOAD', + source_id=file_object.file_id, + source_type='file', + ) + ) self._db_session.commit() return json.dumps({'entries': [file_object]}) @@ -63,7 +73,13 @@ def upload_file(self): ) self._db_session.add(file_object) self._db_session.commit() - self._db_session.add(EventModel(event_type='ITEM_UPLOAD', source_id=file_object.file_id, source_type='file')) + self._db_session.add( + EventModel( + event_type='ITEM_UPLOAD', + source_id=file_object.file_id, + source_type='file', + ) + ) self._db_session.commit() return json.dumps({'entries': [file_object]}) @@ -87,18 +103,30 @@ def update_file_info(self, file_id): parent_folder = get_folder_by_id(self._db_session, parent_id) file_object.parent_id = parent_folder.id self._db_session.add( - EventModel(event_type='ITEM_MOVE', source_id=file_object.file_id, source_type='file'), + EventModel( + event_type='ITEM_MOVE', + source_id=file_object.file_id, + source_type='file', + ), ) else: setattr(file_object, key, value) if key == 'name': self._db_session.add( - EventModel(event_type='ITEM_RENAME', source_id=file_object.file_id, source_type='file'), + EventModel( + event_type='ITEM_RENAME', + source_id=file_object.file_id, + source_type='file', + ), ) elif key == 'sync_state': event_type = 'ITEM_SYNC' if value == 'synced' else 'ITEM_UNSYNC' self._db_session.add( - EventModel(event_type=event_type, source_id=file_object.file_id, source_type='file'), + EventModel( + event_type=event_type, + source_id=file_object.file_id, + source_type='file', + ), ) self._db_session.commit() return json.dumps(file_object) @@ -114,7 +142,13 @@ def copy_file(self, file_id): file_object.parent_id = parent_folder.id self._db_session.add(file_object) self._db_session.commit() - self._db_session.add(EventModel(event_type='ITEM_COPY', source_id=file_object.file_id, source_type='file')) + self._db_session.add( + EventModel( + event_type='ITEM_COPY', + source_id=file_object.file_id, + source_type='file', + ) + ) self._db_session.commit() return json.dumps(file_object) @@ -130,6 +164,12 @@ def delete_file(self, file_id): file_object = get_file_by_id(self._db_session, file_id) self._check_file_lock(file_object) self._db_session.delete(file_object) - self._db_session.add(EventModel(event_type='ITEM_TRASH', source_id=file_object.file_id, source_type='file')) + self._db_session.add( + EventModel( + event_type='ITEM_TRASH', + source_id=file_object.file_id, + source_type='file', + ) + ) self._db_session.commit() response.status = 204 diff --git a/test/functional/mock_box/behavior/folder_behavior.py b/test/functional/mock_box/behavior/folder_behavior.py index 950517a80..27f8b3714 100644 --- a/test/functional/mock_box/behavior/folder_behavior.py +++ b/test/functional/mock_box/behavior/folder_behavior.py @@ -7,7 +7,10 @@ from test.functional.mock_box.db_model.event_model import EventModel from test.functional.mock_box.db_model.file_model import FileModel from test.functional.mock_box.db_model.folder_model import FolderModel -from test.functional.mock_box.util.db_utils import get_folder_by_id, get_user_from_header +from test.functional.mock_box.util.db_utils import ( + get_folder_by_id, + get_user_from_header, +) from test.functional.mock_box.util.http_utils import abort from test.functional.mock_box.util import json_utils as json @@ -32,18 +35,34 @@ def update_folder_info(self, folder_id): parent_folder = get_folder_by_id(self._db_session, parent_id) folder.parent_id = parent_folder.id self._db_session.add( - EventModel(event_type='ITEM_MOVE', source_id=folder.folder_id, source_type='folder'), + EventModel( + event_type='ITEM_MOVE', + source_id=folder.folder_id, + source_type='folder', + ), ) else: setattr(folder, key, value) if key == 'name': self._db_session.add( - EventModel(event_type='ITEM_RENAME', source_id=folder.folder_id, source_type='folder'), + EventModel( + event_type='ITEM_RENAME', + source_id=folder.folder_id, + source_type='folder', + ), ) elif key == 'sync_state': - event_type = 'ITEM_SYNC' if value == FolderSyncState.IS_SYNCED else 'ITEM_UNSYNC' + event_type = ( + 'ITEM_SYNC' + if value == FolderSyncState.IS_SYNCED + else 'ITEM_UNSYNC' + ) self._db_session.add( - EventModel(event_type=event_type, source_id=folder.folder_id, source_type='folder'), + EventModel( + event_type=event_type, + source_id=folder.folder_id, + source_type='folder', + ), ) self._db_session.commit() return json.dumps(folder) @@ -58,7 +77,11 @@ def copy_folder(self, folder_id): folder.parent_id = parent_folder.id self._db_session.add(folder) self._db_session.commit() - self._db_session.add(EventModel(event_type='ITEM_COPY', source_id=folder.folder_id, source_type='folder')) + self._db_session.add( + EventModel( + event_type='ITEM_COPY', source_id=folder.folder_id, source_type='folder' + ) + ) self._db_session.commit() return json.dumps(folder) @@ -66,13 +89,23 @@ def delete_folder(self, folder_id): folder = get_folder_by_id(self._db_session, folder_id) self._db_session.delete(folder) self._db_session.commit() - self._db_session.add(EventModel(event_type='ITEM_TRASH', source_id=folder.folder_id, source_type='folder')) + self._db_session.add( + EventModel( + event_type='ITEM_TRASH', + source_id=folder.folder_id, + source_type='folder', + ) + ) response.status = 204 def _gat(self, folder_id): # pylint:disable=unused-argument files = self._db_session.query(FileModel).all() - folders = self._db_session.query(FolderModel).filter(FolderModel.folder_id != '0').all() + folders = ( + self._db_session.query(FolderModel) + .filter(FolderModel.folder_id != '0') + .all() + ) return json.dumps({'items': files + folders}) def get_folder_items(self, folder_id): @@ -84,17 +117,31 @@ def get_folder_items(self, folder_id): # fields = request.params.get('fields') # fields = set(fields.split(',')) if fields else set() | set(['name']) folder = get_folder_by_id(self._db_session, folder_id) - folder_count = self._db_session.query(FolderModel).filter_by(parent_id=folder.id).count() + folder_count = ( + self._db_session.query(FolderModel).filter_by(parent_id=folder.id).count() + ) folders = [] if folder_count > offset: - folders = self._db_session.query( - FolderModel, - ).filter_by(parent_id=folder.id).limit(limit).offset(offset).all() + folders = ( + self._db_session.query( + FolderModel, + ) + .filter_by(parent_id=folder.id) + .limit(limit) + .offset(offset) + .all() + ) files = [] if len(folders) < limit: limit -= len(folders) offset -= folder_count - files = self._db_session.query(FileModel).filter_by(parent_id=folder.id).limit(limit).offset(offset).all() + files = ( + self._db_session.query(FileModel) + .filter_by(parent_id=folder.id) + .limit(limit) + .offset(offset) + .all() + ) items = folders + files return json.dumps({'total_count': len(items), 'entries': items}) @@ -111,16 +158,26 @@ def create_folder(self): abort(400, 'Missing parameter: parent(id)') parent_id = parent['id'] try: - parent_folder = self._db_session.query(FolderModel).filter_by(folder_id=parent_id).one() + parent_folder = ( + self._db_session.query(FolderModel).filter_by(folder_id=parent_id).one() + ) except NoResultFound: abort(404) owner = get_user_from_header(self._db_session) name_in_use = self._db_session.query(FolderModel).filter_by(name=name).count() if name_in_use: abort(409, 'An item with that name already exists.') - folder = FolderModel(name=name, parent_id=parent_folder.id, owned_by=owner, created_by=owner) + folder = FolderModel( + name=name, parent_id=parent_folder.id, owned_by=owner, created_by=owner + ) self._db_session.add(folder) self._db_session.commit() - self._db_session.add(EventModel(event_type='ITEM_CREATE', source_id=folder.folder_id, source_type='folder')) + self._db_session.add( + EventModel( + event_type='ITEM_CREATE', + source_id=folder.folder_id, + source_type='folder', + ) + ) self._db_session.commit() return json.dumps(folder) diff --git a/test/functional/mock_box/behavior/oauth2_behavior.py b/test/functional/mock_box/behavior/oauth2_behavior.py index d5c792061..e87f3157b 100644 --- a/test/functional/mock_box/behavior/oauth2_behavior.py +++ b/test/functional/mock_box/behavior/oauth2_behavior.py @@ -22,14 +22,24 @@ def __init__(self, db_session): self._auth_request = {} def add_application(self, client_id, client_secret, user_ids): - users = self._db_session.query(UserModel).filter(UserModel.user_id.in_(user_ids)).all() - app = ApplicationModel(client_id=client_id, client_secret=client_secret, users=users) + users = ( + self._db_session.query(UserModel) + .filter(UserModel.user_id.in_(user_ids)) + .all() + ) + app = ApplicationModel( + client_id=client_id, client_secret=client_secret, users=users + ) self._db_session.add(app) self._db_session.commit() def _get_application_by_id(self, client_id): try: - return self._db_session.query(ApplicationModel).filter_by(client_id=client_id).one() + return ( + self._db_session.query(ApplicationModel) + .filter_by(client_id=client_id) + .one() + ) except NoResultFound: abort(400, f'Invalid client id: {client_id}') @@ -60,23 +70,36 @@ def _create_tokens(self, client_id, user_login=None, owned_by_id=None): owned_by_id = user.id app = self._get_application_by_id(client_id) access_token, refresh_token = uuid4().hex, uuid4().hex - access_token_valid_until = datetime.utcnow() + timedelta(seconds=self.ACCESS_TOKEN_DURATION_SECONDS) - refresh_token_valid_until = datetime.utcnow() + timedelta(days=self.REFRESH_TOKEN_DURATION_DAYS) - self._db_session.add(TokenModel( - token=access_token, - expires_at=access_token_valid_until, - authorized_application_id=app.id, - owned_by_id=owned_by_id, - token_type='access' - )) - self._db_session.add(TokenModel( - token=refresh_token, - expires_at=refresh_token_valid_until, - authorized_application_id=app.id, - owned_by_id=owned_by_id, - token_type='refresh' - )) - return access_token, access_token_valid_until, refresh_token, refresh_token_valid_until + access_token_valid_until = datetime.utcnow() + timedelta( + seconds=self.ACCESS_TOKEN_DURATION_SECONDS + ) + refresh_token_valid_until = datetime.utcnow() + timedelta( + days=self.REFRESH_TOKEN_DURATION_DAYS + ) + self._db_session.add( + TokenModel( + token=access_token, + expires_at=access_token_valid_until, + authorized_application_id=app.id, + owned_by_id=owned_by_id, + token_type='access', + ) + ) + self._db_session.add( + TokenModel( + token=refresh_token, + expires_at=refresh_token_valid_until, + authorized_application_id=app.id, + owned_by_id=owned_by_id, + token_type='refresh', + ) + ) + return ( + access_token, + access_token_valid_until, + refresh_token, + refresh_token_valid_until, + ) def oauth2_authorize(self): """ @@ -94,7 +117,10 @@ def oauth2_authorize(self): 'redirect_uri': redirect_uri, 'state': state, } - return {'user_login': user_login, 'action': '{}://{}{}'.format(*request.urlparts[:3])} + return { + 'user_login': user_login, + 'action': '{}://{}{}'.format(*request.urlparts[:3]), + } def oauth2_finish_loop(self): user_login = request.forms.get('login') @@ -114,7 +140,9 @@ def oauth2_token(self): Either exchanges an auth code for an access/refresh token pair, or refreshes a token. """ grant_type = request.forms.get('grant_type') - client_id, client_secret = request.forms.get('client_id'), request.forms.get('client_secret') + client_id, client_secret = request.forms.get('client_id'), request.forms.get( + 'client_secret' + ) app = self._get_application_by_id(client_id) if client_secret != app.client_secret: abort(400, f'Invalid client secret: {client_secret}') @@ -123,10 +151,15 @@ def oauth2_token(self): code = request.forms.get('code') if self._auth_request is None: abort(400, f'Invalid code: {code}') - access_token, refresh_token = self._auth_request['access_token'], self._auth_request['refresh_token'] + access_token, refresh_token = ( + self._auth_request['access_token'], + self._auth_request['refresh_token'], + ) elif grant_type == 'refresh_token': refresh_token = request.forms.get('refresh_token') - refresh_token_record = get_token_record_by_token(self._db_session, refresh_token) + refresh_token_record = get_token_record_by_token( + self._db_session, refresh_token + ) if refresh_token_record.token_type == 'refresh': if datetime.utcnow() > refresh_token_record.expires_at: abort(400, f'Token expired: {refresh_token}') @@ -139,11 +172,13 @@ def oauth2_token(self): else: abort(400, f'Invalid grant type: {grant_type}') - return json.dumps({ - 'access_token': access_token, - 'refresh_token': refresh_token, - 'expires_in': self.ACCESS_TOKEN_DURATION_SECONDS, - }) + return json.dumps( + { + 'access_token': access_token, + 'refresh_token': refresh_token, + 'expires_in': self.ACCESS_TOKEN_DURATION_SECONDS, + } + ) def expire_token(self, token): token_record = get_token_record_by_token(self._db_session, token) diff --git a/test/functional/mock_box/box.py b/test/functional/mock_box/box.py index fbcfcec35..e89ffe02e 100644 --- a/test/functional/mock_box/box.py +++ b/test/functional/mock_box/box.py @@ -12,6 +12,7 @@ from test.functional.mock_box.behavior.oauth2_behavior import OAuth2Behavior from test.functional.mock_box.behavior.user_behavior import UserBehavior from test.functional.mock_box.db_model import DbModel + # pylint:disable=unused-import from test.functional.mock_box.db_model.collaboration_model import CollaborationModel from test.functional.mock_box.db_model.event_model import EventModel @@ -19,7 +20,11 @@ from test.functional.mock_box.db_model.folder_model import FolderModel from test.functional.mock_box.db_model.group_model import GroupModel from test.functional.mock_box.db_model.lock_model import LockModel -from test.functional.mock_box.db_model.share_model import ShareFileModel, ShareFolderModel +from test.functional.mock_box.db_model.share_model import ( + ShareFileModel, + ShareFolderModel, +) + # pylint:enable=unused-import from test.functional.mock_box.db_model.user_model import UserModel from test.functional.mock_box.util.chaos_utils import allow_chaos @@ -36,7 +41,6 @@ StoppableWSGIRefServer, ) - TEMPLATE_PATH.insert(0, join(dirname(__file__), 'views')) @@ -44,6 +48,7 @@ class Box: """ Fake Box. Sets up 4 webservers - one for auth, one for upload, one for events, and one for the rest of the api. """ + API_PORT = 18086 UPLOAD_PORT = 18087 OAUTH_API_PORT = 18088 @@ -59,7 +64,13 @@ def __init__(self): self._db_session_maker = None self.reset_filesystem() # Mock Box consists of 3 webservers - one for the content API, one for the upload API, and one for OAuth2 - api, upload, oauth_api, event, oauth_authorize = Bottle(), Bottle(), Bottle(), Bottle(), Bottle() + api, upload, oauth_api, event, oauth_authorize = ( + Bottle(), + Bottle(), + Bottle(), + Bottle(), + Bottle(), + ) app_mapping = { self.API_PORT: api, self.EVENT_PORT: event, @@ -68,18 +79,30 @@ def __init__(self): self.OAUTH_AUTHORIZE_PORT: oauth_authorize, } # Since we don't instantiate the servers until Box is instantiated, we have to apply the routes now - for routed_method in (getattr(self, m) for m in dir(self) if hasattr(getattr(self, m), 'route')): + for routed_method in ( + getattr(self, m) for m in dir(self) if hasattr(getattr(self, m), 'route') + ): app_port = routed_method.app app = app_mapping[app_port] app.route(routed_method.route, routed_method.verb, routed_method) for code in [400, 401, 404, 409, 429, 500]: for app in app_mapping.values(): app.error(code)(self.handle_error) - self._api = StoppableWSGIRefServer(host='localhost', port=self.API_PORT).run(api) - self._upload = StoppableWSGIRefServer(host='localhost', port=self.UPLOAD_PORT).run(upload) - self._oauth_api = StoppableWSGIRefServer(host='localhost', port=self.OAUTH_API_PORT).run(oauth_api) - self._event = StoppableWSGIRefServer(host='localhost', port=self.EVENT_PORT).run(event) - self._oauth_authorize = StoppableWSGIRefServer(host='localhost', port=self.OAUTH_AUTHORIZE_PORT).run(oauth_authorize) + self._api = StoppableWSGIRefServer(host='localhost', port=self.API_PORT).run( + api + ) + self._upload = StoppableWSGIRefServer( + host='localhost', port=self.UPLOAD_PORT + ).run(upload) + self._oauth_api = StoppableWSGIRefServer( + host='localhost', port=self.OAUTH_API_PORT + ).run(oauth_api) + self._event = StoppableWSGIRefServer( + host='localhost', port=self.EVENT_PORT + ).run(event) + self._oauth_authorize = StoppableWSGIRefServer( + host='localhost', port=self.OAUTH_AUTHORIZE_PORT + ).run(oauth_authorize) self._rate_limit_bucket = (self.RATE_LIMIT_THRESHOLD, datetime.utcnow()) @staticmethod @@ -166,7 +189,9 @@ def check_rate_limits(self): """ capactiy, timestamp = self._rate_limit_bucket now = datetime.utcnow() - delta = self.RATE_LIMIT_REQUEST_PER_SECOND * (now - timestamp).microseconds / 1e6 + delta = ( + self.RATE_LIMIT_REQUEST_PER_SECOND * (now - timestamp).microseconds / 1e6 + ) new_capacity = min(self.RATE_LIMIT_THRESHOLD, capactiy + delta) self._rate_limit_bucket = (new_capacity - 1, now) if new_capacity < 1: diff --git a/test/functional/mock_box/db_model/__init__.py b/test/functional/mock_box/db_model/__init__.py index 79db94586..a4a652f1f 100644 --- a/test/functional/mock_box/db_model/__init__.py +++ b/test/functional/mock_box/db_model/__init__.py @@ -1,4 +1,3 @@ from sqlalchemy.ext.declarative import declarative_base - DbModel = declarative_base() diff --git a/test/functional/mock_box/db_model/application_model.py b/test/functional/mock_box/db_model/application_model.py index 7f852798a..b84d95a59 100644 --- a/test/functional/mock_box/db_model/application_model.py +++ b/test/functional/mock_box/db_model/application_model.py @@ -2,21 +2,29 @@ from sqlalchemy.orm import relationship from test.functional.mock_box.db_model import DbModel - _user_app_table = sqlalchemy.Table( 'user_app_association', DbModel.metadata, - sqlalchemy.Column('app_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_application.id')), - sqlalchemy.Column('user_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) + sqlalchemy.Column( + 'app_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_application.id') + ), + sqlalchemy.Column( + 'user_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ), ) class ApplicationModel(DbModel): """DB Model for Box users.""" + __tablename__ = 'box_application' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name client_id = sqlalchemy.Column(sqlalchemy.String(32), nullable=False) client_secret = sqlalchemy.Column(sqlalchemy.String(32), nullable=False) - auth_tokens = relationship('TokenModel', backref='application', cascade='save-update, delete') + auth_tokens = relationship( + 'TokenModel', backref='application', cascade='save-update, delete' + ) users = relationship('UserModel', secondary=_user_app_table, backref='apps') diff --git a/test/functional/mock_box/db_model/collaboration_model.py b/test/functional/mock_box/db_model/collaboration_model.py index 6ff0a6f9f..725932edf 100644 --- a/test/functional/mock_box/db_model/collaboration_model.py +++ b/test/functional/mock_box/db_model/collaboration_model.py @@ -5,12 +5,21 @@ class CollaborationModel(DbModel): """DB Model for Box collaborations.""" + __tablename__ = 'box_collaboration' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name - collab_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) - item_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id')) + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name + collab_id = sqlalchemy.Column( + sqlalchemy.String(32), default=lambda: uuid.uuid4().hex + ) + item_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id') + ) created_at = sqlalchemy.Column(sqlalchemy.DateTime) modified_at = sqlalchemy.Column(sqlalchemy.DateTime) - created_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) + created_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) status = sqlalchemy.Column(sqlalchemy.String(10)) diff --git a/test/functional/mock_box/db_model/event_model.py b/test/functional/mock_box/db_model/event_model.py index ce8769693..e9d257e95 100644 --- a/test/functional/mock_box/db_model/event_model.py +++ b/test/functional/mock_box/db_model/event_model.py @@ -6,27 +6,36 @@ class EventModel(DbModel): """DB Model for Box events.""" + __tablename__ = 'box_event' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name - event_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name + event_id = sqlalchemy.Column( + sqlalchemy.String(32), default=lambda: uuid.uuid4().hex + ) stream_position = sqlalchemy.Column(sqlalchemy.DateTime, default=datetime.utcnow) - created_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) - event_type = sqlalchemy.Column(sqlalchemy.Enum( - 'ITEM_CREATE', - 'ITEM_UPLOAD', - 'ITEM_MOVE', - 'ITEM_COPY', - 'LOCK_CREATE', - 'LOCK_DESTROY', - 'ITEM_TRASH', - 'ITEM_UNDELETE_VIA_TRASH', - 'COLLAB_ADD_COLLABORATOR', - 'COLLAB_REMOVE_COLLABORATOR', - 'ITEM_SYNC', - 'ITEM_UNSYNC', - 'ITEM_RENAME', - )) + created_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) + event_type = sqlalchemy.Column( + sqlalchemy.Enum( + 'ITEM_CREATE', + 'ITEM_UPLOAD', + 'ITEM_MOVE', + 'ITEM_COPY', + 'LOCK_CREATE', + 'LOCK_DESTROY', + 'ITEM_TRASH', + 'ITEM_UNDELETE_VIA_TRASH', + 'COLLAB_ADD_COLLABORATOR', + 'COLLAB_REMOVE_COLLABORATOR', + 'ITEM_SYNC', + 'ITEM_UNSYNC', + 'ITEM_RENAME', + ) + ) source_id = sqlalchemy.Column(sqlalchemy.String(32)) source_type = sqlalchemy.Column(sqlalchemy.Enum('file', 'folder')) sequence_id = sqlalchemy.Column(sqlalchemy.Integer, nullable=False) diff --git a/test/functional/mock_box/db_model/file_model.py b/test/functional/mock_box/db_model/file_model.py index 58d52d368..0834ba4e1 100644 --- a/test/functional/mock_box/db_model/file_model.py +++ b/test/functional/mock_box/db_model/file_model.py @@ -7,18 +7,29 @@ class FileModel(DbModel): """DB Model for Box files.""" + __tablename__ = 'box_file' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name - file_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: str(uuid.uuid4().int)) + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name + file_id = sqlalchemy.Column( + sqlalchemy.String(32), default=lambda: str(uuid.uuid4().int) + ) name = sqlalchemy.Column(sqlalchemy.String(255)) - parent_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id')) + parent_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id') + ) content = sqlalchemy.Column(sqlalchemy.LargeBinary) created_at = sqlalchemy.Column(sqlalchemy.DateTime, default=datetime.now) modified_at = sqlalchemy.Column(sqlalchemy.DateTime, onupdate=datetime.now) - created_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) + created_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) created_by = relationship('UserModel', foreign_keys=[created_by_id]) - owned_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) + owned_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) shared_link = relationship( 'ShareFileModel', backref=backref('shared_file', remote_side=[id]), diff --git a/test/functional/mock_box/db_model/folder_model.py b/test/functional/mock_box/db_model/folder_model.py index f2bc0691b..baf348ee4 100644 --- a/test/functional/mock_box/db_model/folder_model.py +++ b/test/functional/mock_box/db_model/folder_model.py @@ -7,26 +7,46 @@ class FolderModel(DbModel): """DB Model for Box folders.""" + __tablename__ = 'box_folder' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name - folder_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: str(uuid.uuid4().int)) + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name + folder_id = sqlalchemy.Column( + sqlalchemy.String(32), default=lambda: str(uuid.uuid4().int) + ) name = sqlalchemy.Column(sqlalchemy.String(255)) files = relationship('FileModel', backref='parent', cascade='save-update, delete') - parent_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id')) - folders = relationship('FolderModel', backref=backref('parent', remote_side=[id]), cascade='save-update, delete') - created_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) + parent_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id') + ) + folders = relationship( + 'FolderModel', + backref=backref('parent', remote_side=[id]), + cascade='save-update, delete', + ) + created_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) created_by = relationship('UserModel', foreign_keys=[created_by_id]) created_at = sqlalchemy.Column(sqlalchemy.DateTime, default=datetime.now) modified_at = sqlalchemy.Column(sqlalchemy.DateTime, onupdate=datetime.now) - owned_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) - collaborations = relationship('CollaborationModel', backref='item', cascade='save-update, delete') + owned_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) + collaborations = relationship( + 'CollaborationModel', backref='item', cascade='save-update, delete' + ) shared_link = relationship( 'ShareFolderModel', backref=backref('shared_folder', remote_side=[id]), cascade='save-update, delete', ) - sync_state = sqlalchemy.Column(sqlalchemy.Enum('not_synced', 'partially_synced', 'synced'), default='not_synced') + sync_state = sqlalchemy.Column( + sqlalchemy.Enum('not_synced', 'partially_synced', 'synced'), + default='not_synced', + ) sequence_id = sqlalchemy.Column(sqlalchemy.Integer, nullable=False) etag = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) diff --git a/test/functional/mock_box/db_model/group_model.py b/test/functional/mock_box/db_model/group_model.py index 3a1db3e73..ebf31ffa0 100644 --- a/test/functional/mock_box/db_model/group_model.py +++ b/test/functional/mock_box/db_model/group_model.py @@ -3,20 +3,28 @@ from sqlalchemy.orm import relationship from test.functional.mock_box.db_model import DbModel - _user_group_table = sqlalchemy.Table( 'user_group_association', DbModel.metadata, - sqlalchemy.Column('group_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_group.id')), - sqlalchemy.Column('user_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) + sqlalchemy.Column( + 'group_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_group.id') + ), + sqlalchemy.Column( + 'user_id', sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ), ) class GroupModel(DbModel): """DB Model for Box groups.""" + __tablename__ = 'box_group' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name - group_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name + group_id = sqlalchemy.Column( + sqlalchemy.String(32), default=lambda: uuid.uuid4().hex + ) name = sqlalchemy.Column(sqlalchemy.String(255)) users = relationship('UserModel', secondary=_user_group_table, backref='groups') diff --git a/test/functional/mock_box/db_model/lock_model.py b/test/functional/mock_box/db_model/lock_model.py index e1f6a485d..c5541e1b6 100644 --- a/test/functional/mock_box/db_model/lock_model.py +++ b/test/functional/mock_box/db_model/lock_model.py @@ -5,12 +5,19 @@ class LockModel(DbModel): """DB Model for Box locks.""" + __tablename__ = 'box_lock' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name lock_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) - item_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_file.id')) + item_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_file.id') + ) created_at = sqlalchemy.Column(sqlalchemy.DateTime) expires_at = sqlalchemy.Column(sqlalchemy.DateTime) - created_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) + created_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) is_download_prevented = sqlalchemy.Column(sqlalchemy.Boolean) diff --git a/test/functional/mock_box/db_model/share_model.py b/test/functional/mock_box/db_model/share_model.py index 241d1de31..8a2638502 100644 --- a/test/functional/mock_box/db_model/share_model.py +++ b/test/functional/mock_box/db_model/share_model.py @@ -5,18 +5,29 @@ class ShareModel(DbModel): """DB Model for Box shared links.""" + __tablename__ = 'box_share' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name - share_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name + share_id = sqlalchemy.Column( + sqlalchemy.String(32), default=lambda: uuid.uuid4().hex + ) url = sqlalchemy.Column(sqlalchemy.String(50)) class ShareFileModel(ShareModel): """DB Model for Box shared links to files.""" - file_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_file.id')) + + file_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_file.id') + ) class ShareFolderModel(ShareModel): """DB Model for Box shared links to folders.""" - folder_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id')) + + folder_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_folder.id') + ) diff --git a/test/functional/mock_box/db_model/token_model.py b/test/functional/mock_box/db_model/token_model.py index 096ec7256..6d0467796 100644 --- a/test/functional/mock_box/db_model/token_model.py +++ b/test/functional/mock_box/db_model/token_model.py @@ -5,11 +5,18 @@ class TokenModel(DbModel): """DB Model for Box tokens.""" + __tablename__ = 'box_token' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name - owned_by_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id')) - authorized_application_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey('box_application.id')) + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name + owned_by_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_user.id') + ) + authorized_application_id = sqlalchemy.Column( + sqlalchemy.Integer, sqlalchemy.ForeignKey('box_application.id') + ) expires_at = sqlalchemy.Column(sqlalchemy.DateTime) token_type = sqlalchemy.Column(sqlalchemy.Enum('access', 'refresh'), nullable=False) token = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) diff --git a/test/functional/mock_box/db_model/user_model.py b/test/functional/mock_box/db_model/user_model.py index 13b30bca4..77df81828 100644 --- a/test/functional/mock_box/db_model/user_model.py +++ b/test/functional/mock_box/db_model/user_model.py @@ -6,9 +6,12 @@ class UserModel(DbModel): """DB Model for Box users.""" + __tablename__ = 'box_user' - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True) # pylint:disable=invalid-name + id = sqlalchemy.Column( + sqlalchemy.Integer, primary_key=True, autoincrement=True + ) # pylint:disable=invalid-name user_id = sqlalchemy.Column(sqlalchemy.String(32), default=lambda: uuid.uuid4().hex) name = sqlalchemy.Column(sqlalchemy.String(255)) login = sqlalchemy.Column(sqlalchemy.String(255)) @@ -24,6 +27,12 @@ class UserModel(DbModel): cascade='save-update, delete', foreign_keys='[FolderModel.owned_by_id]', ) - created_collaborations = relationship('CollaborationModel', backref='created_by', cascade='save-update, delete') - created_locks = relationship('LockModel', backref='created_by', cascade='save-update, delete') - authorized_application_tokens = relationship('TokenModel', backref='owned_by', cascade='save-update, delete') + created_collaborations = relationship( + 'CollaborationModel', backref='created_by', cascade='save-update, delete' + ) + created_locks = relationship( + 'LockModel', backref='created_by', cascade='save-update, delete' + ) + authorized_application_tokens = relationship( + 'TokenModel', backref='owned_by', cascade='save-update, delete' + ) diff --git a/test/functional/mock_box/util/chaos_utils.py b/test/functional/mock_box/util/chaos_utils.py index a844b843b..949e329f9 100644 --- a/test/functional/mock_box/util/chaos_utils.py +++ b/test/functional/mock_box/util/chaos_utils.py @@ -45,19 +45,27 @@ def chaotic_method(self, *args, **kwargs): def delay(delay_seconds): - return lambda delayed_function: lambda *args, **kwargs: sleep(delay_seconds) or delayed_function(*args, **kwargs) + return lambda delayed_function: lambda *args, **kwargs: sleep( + delay_seconds + ) or delayed_function(*args, **kwargs) def error(code, message=None, headers=None): - return lambda erroneous_function: lambda *args, **kwargs: abort(code, message=message, headers=headers) + return lambda erroneous_function: lambda *args, **kwargs: abort( + code, message=message, headers=headers + ) def html(method): - return lambda *args, **kwargs: template('html_response', response=method(*args, **kwargs)) + return lambda *args, **kwargs: template( + 'html_response', response=method(*args, **kwargs) + ) def xml(method): - return lambda *args, **kwargs: template('xml_response', response=method(*args, **kwargs)) + return lambda *args, **kwargs: template( + 'xml_response', response=method(*args, **kwargs) + ) def patch(operations): @@ -78,5 +86,7 @@ def patched_inner(*args, **kwargs): if not_json: return_value = json.loads(return_value) return return_value + return patched_inner + return inner diff --git a/test/functional/mock_box/util/http_utils.py b/test/functional/mock_box/util/http_utils.py index 8dba62656..3d21922a5 100644 --- a/test/functional/mock_box/util/http_utils.py +++ b/test/functional/mock_box/util/http_utils.py @@ -2,7 +2,6 @@ from functools import partial, wraps from threading import Thread - RETRY_AFTER_HEADER = str('Retry-After') @@ -25,44 +24,52 @@ def retry_after(delay, code=429): def authorize(method): """Decorator for a method that requires authorization. Unauthorized requests will be aborted with a 401.""" + @wraps(method) def authorized_method(self, *args, **kwargs): skip_auth = kwargs.pop('skip_auth', False) if not skip_auth: self.check_authorization_header() return method(self, *args, **kwargs) + return authorized_method def rate_limit(method): """Decorator for a method that requires rate limiting. Too many requests will be aborted with a 429.""" + @wraps(method) def limited_method(self, *args, **kwargs): skip_limit = kwargs.pop('skip_limit', False) if not skip_limit: self.check_rate_limits() return method(self, *args, **kwargs) + return limited_method def _route(verb, app, route): """Helper decorator to apply methods to routes.""" + def routed_method(method): setattr(method, 'verb', verb) setattr(method, 'app', app) setattr(method, 'route', route) return method + return routed_method def log_request(method): """Decorator for a method to add its request to the request log.""" + @wraps(method) def logged_method(self, *args, **kwargs): skip_log = kwargs.pop('skip_log', False) if not skip_log: self.append_to_request_log() return method(self, *args, **kwargs) + return logged_method @@ -78,6 +85,7 @@ class StoppableWSGIRefServer(ServerAdapter): Subclass of built-in Bottle server adapter that allows the server to be stopped. This is important for testing, since we don't want to "serve forever". """ + def __init__(self, host='127.0.0.1', port=8080, **options): super().__init__(host, port, **options) self.srv = None diff --git a/test/functional/mock_box/util/json_utils.py b/test/functional/mock_box/util/json_utils.py index b44fa0ee8..ca8ab418b 100644 --- a/test/functional/mock_box/util/json_utils.py +++ b/test/functional/mock_box/util/json_utils.py @@ -18,7 +18,11 @@ def default(self, o): if DbModel not in self._cls.__bases__: self._cls = o.__class__ fields = {} - for field in (x for x in dir(o) if not x.startswith('_') and x not in self._denylist): + for field in ( + x + for x in dir(o) + if not x.startswith('_') and x not in self._denylist + ): data = o.__getattribute__(field) if isinstance(o, self._cls): fields[field] = data @@ -52,8 +56,16 @@ def load(file_pointer, **kwargs): def dump(obj, file_pointer, **kwargs): - return json.dump(obj, file_pointer, cls=serializer_factory(obj.__class__), check_circular=False, **kwargs) + return json.dump( + obj, + file_pointer, + cls=serializer_factory(obj.__class__), + check_circular=False, + **kwargs + ) def dumps(obj, **kwargs): - return json.dumps(obj, cls=serializer_factory(obj.__class__), check_circular=False, **kwargs) + return json.dumps( + obj, cls=serializer_factory(obj.__class__), check_circular=False, **kwargs + ) diff --git a/test/functional/test_delete.py b/test/functional/test_delete.py index 80e2af5b7..95ffe9cff 100644 --- a/test/functional/test_delete.py +++ b/test/functional/test_delete.py @@ -8,7 +8,11 @@ def test_upload_then_delete(box_client, test_file_path, test_file_content, file_name): - with patch('boxsdk.object.folder.open', streamable_mock_open(read_data=test_file_content), create=True): + with patch( + 'boxsdk.object.folder.open', + streamable_mock_open(read_data=test_file_content), + create=True, + ): file_object = box_client.folder('0').upload(test_file_path, file_name) assert file_object.delete() folder_items = box_client.folder('0').get_items(1) diff --git a/test/functional/test_events.py b/test/functional/test_events.py index f158ff35a..3dc97816c 100644 --- a/test/functional/test_events.py +++ b/test/functional/test_events.py @@ -54,55 +54,96 @@ def test_create_folder_causes_create_event(created_subfolder, assert_event): assert_event(lambda: created_subfolder, 'ITEM_CREATE') -def test_move_file_causes_move_event(box_events, move_target, uploaded_file, assert_event): +def test_move_file_causes_move_event( + box_events, move_target, uploaded_file, assert_event +): # pylint:disable=redefined-outer-name - assert_event(lambda: uploaded_file.move(move_target), 'ITEM_MOVE', box_events.get_latest_stream_position()) + assert_event( + lambda: uploaded_file.move(move_target), + 'ITEM_MOVE', + box_events.get_latest_stream_position(), + ) -def test_move_folder_causes_move_event(box_events, move_target, created_subfolder, assert_event): +def test_move_folder_causes_move_event( + box_events, move_target, created_subfolder, assert_event +): # pylint:disable=redefined-outer-name - assert_event(lambda: created_subfolder.move(move_target), 'ITEM_MOVE', box_events.get_latest_stream_position()) + assert_event( + lambda: created_subfolder.move(move_target), + 'ITEM_MOVE', + box_events.get_latest_stream_position(), + ) def test_rename_file_causes_rename_event(box_events, uploaded_file, assert_event): # pylint:disable=redefined-outer-name updated_name = f'updated_{uploaded_file.name}' - assert_event(lambda: uploaded_file.rename(updated_name), 'ITEM_RENAME', box_events.get_latest_stream_position()) + assert_event( + lambda: uploaded_file.rename(updated_name), + 'ITEM_RENAME', + box_events.get_latest_stream_position(), + ) def test_rename_folder_causes_rename_event(box_events, created_subfolder, assert_event): # pylint:disable=redefined-outer-name updated_name = f'updated_{created_subfolder.name}' - assert_event(lambda: created_subfolder.rename(updated_name), 'ITEM_RENAME', box_events.get_latest_stream_position()) + assert_event( + lambda: created_subfolder.rename(updated_name), + 'ITEM_RENAME', + box_events.get_latest_stream_position(), + ) -def test_copy_file_causes_copy_event(box_events, copy_target, uploaded_file, assert_event): +def test_copy_file_causes_copy_event( + box_events, copy_target, uploaded_file, assert_event +): # pylint:disable=redefined-outer-name - assert_event(lambda: uploaded_file.copy(parent_folder=copy_target), 'ITEM_COPY', box_events.get_latest_stream_position()) + assert_event( + lambda: uploaded_file.copy(parent_folder=copy_target), + 'ITEM_COPY', + box_events.get_latest_stream_position(), + ) -def test_copy_folder_causes_copy_event(box_events, copy_target, created_subfolder, assert_event): +def test_copy_folder_causes_copy_event( + box_events, copy_target, created_subfolder, assert_event +): # pylint:disable=redefined-outer-name - assert_event(lambda: created_subfolder.copy(parent_folder=copy_target), 'ITEM_COPY', box_events.get_latest_stream_position()) + assert_event( + lambda: created_subfolder.copy(parent_folder=copy_target), + 'ITEM_COPY', + box_events.get_latest_stream_position(), + ) @pytest.mark.xfail(reason='trash event has no source') def test_delete_file_causes_trash_event(box_events, uploaded_file, assert_event): # pylint:disable=redefined-outer-name - assert_event(uploaded_file.delete, 'ITEM_TRASH', box_events.get_latest_stream_position()) + assert_event( + uploaded_file.delete, 'ITEM_TRASH', box_events.get_latest_stream_position() + ) @pytest.mark.xfail(reason='trash event has no source') def test_delete_folder_causes_trash_event(box_events, created_subfolder, assert_event): # pylint:disable=redefined-outer-name - assert_event(created_subfolder.delete, 'ITEM_TRASH', box_events.get_latest_stream_position()) + assert_event( + created_subfolder.delete, 'ITEM_TRASH', box_events.get_latest_stream_position() + ) -@pytest.mark.parametrize('sync_state,event_type', [ - (FolderSyncState.IS_SYNCED, 'ITEM_SYNC'), - (FolderSyncState.NOT_SYNCED, 'ITEM_UNSYNC'), -]) -def test_sync_folder_causes_sync_event(box_events, created_subfolder, assert_event, sync_state, event_type): +@pytest.mark.parametrize( + 'sync_state,event_type', + [ + (FolderSyncState.IS_SYNCED, 'ITEM_SYNC'), + (FolderSyncState.NOT_SYNCED, 'ITEM_UNSYNC'), + ], +) +def test_sync_folder_causes_sync_event( + box_events, created_subfolder, assert_event, sync_state, event_type +): # pylint:disable=redefined-outer-name assert_event( lambda: created_subfolder.update_sync_state(sync_state.value), @@ -145,7 +186,9 @@ def fin(): return long_poll_thread -def test_generate_events_with_long_polling(long_poll_generator, created_subfolder, uploaded_file): +def test_generate_events_with_long_polling( + long_poll_generator, created_subfolder, uploaded_file +): # pylint:disable=redefined-outer-name long_poll_generator.event_ready.wait() long_poll_generator.event_ready.clear() @@ -156,8 +199,12 @@ def test_generate_events_with_long_polling(long_poll_generator, created_subfolde assert not long_poll_generator.event_ready.wait(timeout=0.01) assert len(long_poll_generator.events) == 2 - folder_event = next(e for e in long_poll_generator.events if e['source']['type'] == 'folder') - file_event = next(e for e in long_poll_generator.events if e['source']['type'] == 'file') + folder_event = next( + e for e in long_poll_generator.events if e['source']['type'] == 'folder' + ) + file_event = next( + e for e in long_poll_generator.events if e['source']['type'] == 'file' + ) assert folder_event['event_type'] == 'ITEM_CREATE' assert file_event['event_type'] == 'ITEM_UPLOAD' assert folder_event['source']['id'] == created_subfolder.id @@ -179,8 +226,12 @@ def test_generate_events_with_long_polling(long_poll_generator, created_subfolde assert not long_poll_generator.event_ready.wait(timeout=0.01) assert len(long_poll_generator.events) == 2 - folder_event = next(e for e in long_poll_generator.events if e['source']['type'] == 'folder') - file_event = next(e for e in long_poll_generator.events if e['source']['type'] == 'file') + folder_event = next( + e for e in long_poll_generator.events if e['source']['type'] == 'folder' + ) + file_event = next( + e for e in long_poll_generator.events if e['source']['type'] == 'file' + ) assert folder_event['event_type'] == 'ITEM_RENAME' assert file_event['event_type'] == 'ITEM_RENAME' assert folder_event['source']['id'] == created_subfolder.id diff --git a/test/functional/test_file_upload_update_download.py b/test/functional/test_file_upload_update_download.py index ac1ec3ba3..fefa20ff1 100644 --- a/test/functional/test_file_upload_update_download.py +++ b/test/functional/test_file_upload_update_download.py @@ -20,16 +20,25 @@ def mocked_logger(logger): yield -def test_upload_then_update(box_client, test_file_path, test_file_content, update_file_content, file_name): - with patch('boxsdk.object.folder.open', streamable_mock_open(read_data=test_file_content), create=True): +def test_upload_then_update( + box_client, test_file_path, test_file_content, update_file_content, file_name +): + with patch( + 'boxsdk.object.folder.open', + streamable_mock_open(read_data=test_file_content), + create=True, + ): file_object = box_client.folder('0').upload(test_file_path, file_name) assert file_object.name == file_name file_object_with_info = file_object.get() assert file_object_with_info.id == file_object.object_id assert file_object_with_info.name == file_name file_content = file_object.content() - expected_file_content = test_file_content.encode('utf-8') if isinstance(test_file_content, str)\ + expected_file_content = ( + test_file_content.encode('utf-8') + if isinstance(test_file_content, str) else test_file_content + ) assert file_content == expected_file_content folder_items = box_client.folder('0').get_items(100) item = folder_items.next() @@ -39,15 +48,22 @@ def test_upload_then_update(box_client, test_file_path, test_file_content, updat assert item_count == 1 assert item.object_id == file_object.object_id assert item.name == file_object.name - with patch('boxsdk.object.file.open', streamable_mock_open(read_data=update_file_content), create=True): + with patch( + 'boxsdk.object.file.open', + streamable_mock_open(read_data=update_file_content), + create=True, + ): updated_file_object = file_object.update_contents(test_file_path) assert updated_file_object.name == file_name file_object_with_info = updated_file_object.get() assert file_object_with_info.id == updated_file_object.object_id assert file_object_with_info.name == file_name file_content = updated_file_object.content() - expected_file_content = update_file_content.encode('utf-8') if isinstance(update_file_content, str)\ + expected_file_content = ( + update_file_content.encode('utf-8') + if isinstance(update_file_content, str) else update_file_content + ) assert file_content == expected_file_content folder_items = box_client.folder('0').get_items(100) item = folder_items.next() @@ -60,35 +76,66 @@ def test_upload_then_update(box_client, test_file_path, test_file_content, updat def test_upload_then_download(box_client, test_file_path, test_file_content, file_name): - with patch('boxsdk.object.folder.open', streamable_mock_open(read_data=test_file_content), create=True): + with patch( + 'boxsdk.object.folder.open', + streamable_mock_open(read_data=test_file_content), + create=True, + ): file_object = box_client.folder('0').upload(test_file_path, file_name) writeable_stream = BytesIO() file_object.download_to(writeable_stream) - expected_file_content = test_file_content.encode('utf-8') if isinstance(test_file_content, str)\ + expected_file_content = ( + test_file_content.encode('utf-8') + if isinstance(test_file_content, str) else test_file_content + ) assert writeable_stream.getvalue() == expected_file_content -def test_do_not_log_downloaded_file_content_stream(box_client, test_file_path, test_file_content, file_name, logger): - with patch('boxsdk.object.folder.open', streamable_mock_open(read_data=test_file_content), create=True): +def test_do_not_log_downloaded_file_content_stream( + box_client, test_file_path, test_file_content, file_name, logger +): + with patch( + 'boxsdk.object.folder.open', + streamable_mock_open(read_data=test_file_content), + create=True, + ): file_object = box_client.folder('0').upload(test_file_path, file_name) writeable_stream = BytesIO() file_object.download_to(writeable_stream) - expected_file_content = test_file_content.encode('utf-8') if isinstance(test_file_content, str) \ + expected_file_content = ( + test_file_content.encode('utf-8') + if isinstance(test_file_content, str) else test_file_content + ) assert writeable_stream.getvalue() == expected_file_content - assert logger.info.call_args[0][1]['content'] == DefaultNetworkResponse.CONTENT_NOT_LOGGED - - -def test_do_not_log_content_of_downloaded_file(box_client, test_file_path, test_file_content, file_name, logger): - with patch('boxsdk.object.folder.open', streamable_mock_open(read_data=test_file_content), create=True): + assert ( + logger.info.call_args[0][1]['content'] + == DefaultNetworkResponse.CONTENT_NOT_LOGGED + ) + + +def test_do_not_log_content_of_downloaded_file( + box_client, test_file_path, test_file_content, file_name, logger +): + with patch( + 'boxsdk.object.folder.open', + streamable_mock_open(read_data=test_file_content), + create=True, + ): file_object = box_client.folder('0').upload(test_file_path, file_name) file_content = file_object.content() - expected_file_content = test_file_content.encode('utf-8') if isinstance(test_file_content, str) \ + expected_file_content = ( + test_file_content.encode('utf-8') + if isinstance(test_file_content, str) else test_file_content + ) assert file_content == expected_file_content - assert logger.info.call_args[0][1]['content'] == DefaultNetworkResponse.CONTENT_NOT_LOGGED + assert ( + logger.info.call_args[0][1]['content'] + == DefaultNetworkResponse.CONTENT_NOT_LOGGED + ) diff --git a/test/functional/test_object_clone.py b/test/functional/test_object_clone.py index 11913e99f..df97f4731 100644 --- a/test/functional/test_object_clone.py +++ b/test/functional/test_object_clone.py @@ -7,14 +7,18 @@ def extra_network_parameters(): return {'timeout': 1} -def test_folder_clone_during_create_subfolder(created_subfolder, extra_network_parameters): +def test_folder_clone_during_create_subfolder( + created_subfolder, extra_network_parameters +): # pylint:disable=redefined-outer-name # pylint:disable=protected-access original_folder = created_subfolder original_folder_id = original_folder._object_id original_folder_response_object = original_folder._response_object original_session = original_folder._session - returned_folder = original_folder.create_subfolder('subfolder', extra_network_parameters=extra_network_parameters) + returned_folder = original_folder.create_subfolder( + 'subfolder', extra_network_parameters=extra_network_parameters + ) returned_session = returned_folder._session assert original_session is not returned_session @@ -24,14 +28,18 @@ def test_folder_clone_during_create_subfolder(created_subfolder, extra_network_p assert original_folder._response_object == original_folder_response_object -def test_folder_clone_during_update_sync_state(created_subfolder, extra_network_parameters): +def test_folder_clone_during_update_sync_state( + created_subfolder, extra_network_parameters +): # pylint:disable=redefined-outer-name # pylint:disable=protected-access original_folder = created_subfolder original_folder_id = original_folder._object_id original_folder_response_object = original_folder._response_object original_session = original_folder._session - returned_folder = original_folder.update_sync_state(FolderSyncState.IS_SYNCED, extra_network_parameters=extra_network_parameters) + returned_folder = original_folder.update_sync_state( + FolderSyncState.IS_SYNCED, extra_network_parameters=extra_network_parameters + ) returned_session = returned_folder._session assert original_session is not returned_session diff --git a/test/functional/test_recovery.py b/test/functional/test_recovery.py index 8548ae154..f35cff51f 100644 --- a/test/functional/test_recovery.py +++ b/test/functional/test_recovery.py @@ -54,7 +54,9 @@ def test_client_retries_on_server_error(box_client, mock_box, error_code, should def test_client_retries_on_retry_after(box_client, mock_box, retry_code, should_apply): # pylint:disable=redefined-outer-name should_apply, expected_num_requests = should_apply - mock_box.get_folder_info.add_chaos(error(retry_code, headers={RETRY_AFTER_HEADER: 1}), should_apply) + mock_box.get_folder_info.add_chaos( + error(retry_code, headers={RETRY_AFTER_HEADER: 1}), should_apply + ) box_client.folder('0').get() assert len(mock_box.requests) == expected_num_requests diff --git a/test/functional/test_token_refresh.py b/test/functional/test_token_refresh.py index b27e2b943..708c8939a 100644 --- a/test/functional/test_token_refresh.py +++ b/test/functional/test_token_refresh.py @@ -7,7 +7,9 @@ def test_expired_access_token_is_refreshed(box_oauth, box_client, mock_box): mock_box.oauth.expire_token(box_oauth._access_token) # pylint:enable=protected-access box_client.folder('0').get() - assert len(mock_box.requests) == 6 # GET /authorize, POST /authorize, /token, get_info, refresh /token, get_info + assert ( + len(mock_box.requests) == 6 + ) # GET /authorize, POST /authorize, /token, get_info, refresh /token, get_info def test_expired_refresh_token_raises(box_oauth, box_client, mock_box): diff --git a/test/integration/conftest.py b/test/integration/conftest.py index 1490d410b..642ba843a 100644 --- a/test/integration/conftest.py +++ b/test/integration/conftest.py @@ -15,7 +15,9 @@ def box_client(box_oauth, mock_box_session): @pytest.fixture() -def box_oauth(unauthorized_session, client_id, client_secret, access_token, refresh_token): +def box_oauth( + unauthorized_session, client_id, client_secret, access_token, refresh_token +): # pylint:disable=redefined-outer-name return OAuth2( client_id, diff --git a/test/integration/mock_network.py b/test/integration/mock_network.py index 1fb777e86..8a8049e36 100644 --- a/test/integration/mock_network.py +++ b/test/integration/mock_network.py @@ -16,7 +16,9 @@ def request(self, method, url, access_token, **kwargs): """Base class override. Make a mock network request using a mock requests.Session. """ - return DefaultNetworkResponse(self._session.request(method, url, **kwargs), access_token) + return DefaultNetworkResponse( + self._session.request(method, url, **kwargs), access_token + ) def retry_after(self, delay, request_method, *args, **kwargs): """Base class override. diff --git a/test/integration/test_as_user.py b/test/integration/test_as_user.py index 94766b6b0..d06625422 100644 --- a/test/integration/test_as_user.py +++ b/test/integration/test_as_user.py @@ -15,11 +15,11 @@ def as_user_headers(mock_user_id, access_token): def test_client_as_user_causes_as_user_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - mock_user_id, - as_user_headers, + box_client, + mock_box_network, + generic_successful_response, + mock_user_id, + as_user_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [generic_successful_response] @@ -36,11 +36,11 @@ def test_client_as_user_causes_as_user_header_to_be_added( def test_folder_object_as_user_causes_as_user_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - mock_user_id, - as_user_headers, + box_client, + mock_box_network, + generic_successful_response, + mock_user_id, + as_user_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ @@ -59,11 +59,11 @@ def test_folder_object_as_user_causes_as_user_header_to_be_added( def test_group_membership_object_as_user_causes_as_user_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - mock_user_id, - as_user_headers, + box_client, + mock_box_network, + generic_successful_response, + mock_user_id, + as_user_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ @@ -82,11 +82,11 @@ def test_group_membership_object_as_user_causes_as_user_header_to_be_added( def test_events_endpoint_as_user_causes_as_user_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - mock_user_id, - as_user_headers, + box_client, + mock_box_network, + generic_successful_response, + mock_user_id, + as_user_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ @@ -94,7 +94,9 @@ def test_events_endpoint_as_user_causes_as_user_header_to_be_added( ] stream_position = 1348790499819 options = {'url': f'{API.BASE_API_URL}/events', 'retry_timeout': 60} - box_client.events().as_user(User(None, mock_user_id)).long_poll(options, stream_position) + box_client.events().as_user(User(None, mock_user_id)).long_poll( + options, stream_position + ) assert mock_box_network.session.request.mock_calls == [ call( 'GET', @@ -108,11 +110,11 @@ def test_events_endpoint_as_user_causes_as_user_header_to_be_added( def test_metadata_endpoint_as_user_causes_as_user_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - mock_user_id, - as_user_headers, + box_client, + mock_box_network, + generic_successful_response, + mock_user_id, + as_user_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ diff --git a/test/integration/test_retry_and_refresh.py b/test/integration/test_retry_and_refresh.py index 0117419da..b2741d4f9 100644 --- a/test/integration/test_retry_and_refresh.py +++ b/test/integration/test_retry_and_refresh.py @@ -3,11 +3,11 @@ def test_automatic_refresh( - box_client, - mock_box_network, - generic_successful_response, - successful_token_mock, - unauthorized_response, + box_client, + mock_box_network, + generic_successful_response, + successful_token_mock, + unauthorized_response, ): mock_box_network.session.request.side_effect = [ unauthorized_response, @@ -27,7 +27,11 @@ def test_automatic_refresh( 'POST', f'{API.OAUTH2_API_URL}/token', data=ANY, - headers={'content-type': 'application/x-www-form-urlencoded', 'User-Agent': ANY, 'X-Box-UA': ANY}, + headers={ + 'content-type': 'application/x-www-form-urlencoded', + 'User-Agent': ANY, + 'X-Box-UA': ANY, + }, log_response_content=True, ), call( diff --git a/test/integration/test_with_shared_link.py b/test/integration/test_with_shared_link.py index 7a0d9a253..e15e456fc 100644 --- a/test/integration/test_with_shared_link.py +++ b/test/integration/test_with_shared_link.py @@ -27,12 +27,12 @@ def box_api_headers(shared_link, shared_link_password, access_token): def test_client_with_shared_link_causes_box_api_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - shared_link, - shared_link_password, - box_api_headers, + box_client, + mock_box_network, + generic_successful_response, + shared_link, + shared_link_password, + box_api_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ @@ -51,12 +51,12 @@ def test_client_with_shared_link_causes_box_api_header_to_be_added( def test_folder_object_with_shared_link_causes_box_api_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - shared_link, - shared_link_password, - box_api_headers, + box_client, + mock_box_network, + generic_successful_response, + shared_link, + shared_link_password, + box_api_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ @@ -75,18 +75,20 @@ def test_folder_object_with_shared_link_causes_box_api_header_to_be_added( def test_group_membership_object_with_shared_link_causes_box_api_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - shared_link, - shared_link_password, - box_api_headers, + box_client, + mock_box_network, + generic_successful_response, + shared_link, + shared_link_password, + box_api_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ generic_successful_response, ] - box_client.group_membership('0').with_shared_link(shared_link, shared_link_password).get() + box_client.group_membership('0').with_shared_link( + shared_link, shared_link_password + ).get() assert mock_box_network.session.request.mock_calls == [ call( 'GET', @@ -99,12 +101,12 @@ def test_group_membership_object_with_shared_link_causes_box_api_header_to_be_ad def test_events_endpoint_with_shared_link_causes_box_api_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - shared_link, - shared_link_password, - box_api_headers, + box_client, + mock_box_network, + generic_successful_response, + shared_link, + shared_link_password, + box_api_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ @@ -112,7 +114,9 @@ def test_events_endpoint_with_shared_link_causes_box_api_header_to_be_added( ] stream_position = 1348790499819 options = {'url': f'{API.BASE_API_URL}/events', 'retry_timeout': 60} - box_client.events().with_shared_link(shared_link, shared_link_password).long_poll(options, stream_position) + box_client.events().with_shared_link(shared_link, shared_link_password).long_poll( + options, stream_position + ) assert mock_box_network.session.request.mock_calls == [ call( 'GET', @@ -126,18 +130,20 @@ def test_events_endpoint_with_shared_link_causes_box_api_header_to_be_added( def test_metadata_endpoint_with_shared_link_causes_box_api_header_to_be_added( - box_client, - mock_box_network, - generic_successful_response, - shared_link, - shared_link_password, - box_api_headers, + box_client, + mock_box_network, + generic_successful_response, + shared_link, + shared_link_password, + box_api_headers, ): # pylint:disable=redefined-outer-name mock_box_network.session.request.side_effect = [ generic_successful_response, ] - box_client.file('0').metadata().with_shared_link(shared_link, shared_link_password).get() + box_client.file('0').metadata().with_shared_link( + shared_link, shared_link_password + ).get() assert mock_box_network.session.request.mock_calls == [ call( 'GET', diff --git a/test/integration_new/__init__.py b/test/integration_new/__init__.py index 5044e8fd2..236054821 100644 --- a/test/integration_new/__init__.py +++ b/test/integration_new/__init__.py @@ -8,10 +8,11 @@ from boxsdk.auth.jwt_auth import JWTAuth from boxsdk.client import Client - JWT_CONFIG_ENV_VAR_NAME = 'JWT_CONFIG_BASE_64' ADMIN_USER_ID_ENV_VAR_NAME = 'ADMIN_USER_ID' -CURRENT_DIR_PATH = str(Path(os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))))) +CURRENT_DIR_PATH = str( + Path(os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))) +) CONFIG_PATH = f'{CURRENT_DIR_PATH}/integration_tests.cfg' CONFIG_PARSER = configparser.ConfigParser() CONFIG_PARSER.read(CONFIG_PATH) @@ -28,7 +29,9 @@ def get_jwt_config() -> JWTAuth: def get_admin_user_id() -> str: - admin_user_id = os.getenv(ADMIN_USER_ID_ENV_VAR_NAME) or CONFIG_PARSER["JWT"].get('AdminUserID') + admin_user_id = os.getenv(ADMIN_USER_ID_ENV_VAR_NAME) or CONFIG_PARSER["JWT"].get( + 'AdminUserID' + ) if not admin_user_id: raise RuntimeError( diff --git a/test/integration_new/context_managers/box_metadata_template.py b/test/integration_new/context_managers/box_metadata_template.py index 8ffc27bb0..46208dd69 100644 --- a/test/integration_new/context_managers/box_metadata_template.py +++ b/test/integration_new/context_managers/box_metadata_template.py @@ -1,6 +1,10 @@ from typing import Any, Iterable -from boxsdk.object.metadata_template import MetadataTemplate, MetadataField, MetadataFieldType +from boxsdk.object.metadata_template import ( + MetadataTemplate, + MetadataField, + MetadataFieldType, +) from test.integration_new import CLIENT @@ -9,7 +13,9 @@ class BoxTestMetadataTemplate: def __init__(self, *, display_name: str, fields: Iterable = None): if fields is None: fields = [] - self._metadata_template: MetadataTemplate = CLIENT.create_metadata_template(display_name=display_name, fields=fields) + self._metadata_template: MetadataTemplate = CLIENT.create_metadata_template( + display_name=display_name, fields=fields + ) def __enter__(self) -> MetadataTemplate: return self._metadata_template diff --git a/test/integration_new/context_managers/box_retention_policy.py b/test/integration_new/context_managers/box_retention_policy.py index 3086ded85..5dd4526e6 100644 --- a/test/integration_new/context_managers/box_retention_policy.py +++ b/test/integration_new/context_managers/box_retention_policy.py @@ -6,28 +6,36 @@ class BoxRetentionPolicy: - DEFAULT_RETENTION_POLICY_NAME = "modifiable_retention_policy_for_integration_tests_extendable_by_owner" + DEFAULT_RETENTION_POLICY_NAME = ( + "modifiable_retention_policy_for_integration_tests_extendable_by_owner" + ) def __init__( - self, - *, - name: str = DEFAULT_RETENTION_POLICY_NAME, - disposition_action: str = 'permanently_delete', - retention_length: Union[float, int] = 1 + self, + *, + name: str = DEFAULT_RETENTION_POLICY_NAME, + disposition_action: str = 'permanently_delete', + retention_length: Union[float, int] = 1 ): policy_type = 'finite' if retention_length < float('inf') else 'infinite' - policies_found = list(USER_CLIENT.get_retention_policies(policy_name=name, policy_type=policy_type)) + policies_found = list( + USER_CLIENT.get_retention_policies( + policy_name=name, policy_type=policy_type + ) + ) if policies_found and policies_found[0].get().status == 'active': self._retention_policy = policies_found[0] else: - self._retention_policy: RetentionPolicy = USER_CLIENT.create_retention_policy( - policy_name=name, - disposition_action=disposition_action, - retention_length=retention_length, - can_owner_extend_retention=True, - retention_type='modifiable' + self._retention_policy: RetentionPolicy = ( + USER_CLIENT.create_retention_policy( + policy_name=name, + disposition_action=disposition_action, + retention_length=retention_length, + can_owner_extend_retention=True, + retention_type='modifiable', + ) ) def __enter__(self) -> RetentionPolicy: diff --git a/test/integration_new/context_managers/box_retention_policy_assigment.py b/test/integration_new/context_managers/box_retention_policy_assigment.py index 7fb1eecc2..8d2d51a84 100644 --- a/test/integration_new/context_managers/box_retention_policy_assigment.py +++ b/test/integration_new/context_managers/box_retention_policy_assigment.py @@ -10,9 +10,9 @@ class BoxRetentionPolicyAssignment: def __init__( - self, - retention_policy: RetentionPolicy, - assignee: Union['Folder', 'Enterprise', 'MetadataTemplate'] + self, + retention_policy: RetentionPolicy, + assignee: Union['Folder', 'Enterprise', 'MetadataTemplate'], ): self._retention_policy_assignment = retention_policy.assign(assignee) diff --git a/test/integration_new/context_managers/box_sign_request.py b/test/integration_new/context_managers/box_sign_request.py index adab894b8..4152b9a60 100644 --- a/test/integration_new/context_managers/box_sign_request.py +++ b/test/integration_new/context_managers/box_sign_request.py @@ -7,7 +7,9 @@ class BoxTestSignRequest: def __init__(self, *, files: Iterable, signers: Iterable, parent_folder_id: str): - self._sign_request = CLIENT.create_sign_request_v2(signers, files, parent_folder_id) + self._sign_request = CLIENT.create_sign_request_v2( + signers, files, parent_folder_id + ) def __enter__(self) -> WebLink: return self._sign_request diff --git a/test/integration_new/context_managers/box_test_file.py b/test/integration_new/context_managers/box_test_file.py index 49d42ebbe..f70bb741e 100644 --- a/test/integration_new/context_managers/box_test_file.py +++ b/test/integration_new/context_managers/box_test_file.py @@ -8,7 +8,13 @@ class BoxTestFile: - def __init__(self, *, file_path: str, name: str = None, parent_folder: Optional[Folder] = None): + def __init__( + self, + *, + file_path: str, + name: str = None, + parent_folder: Optional[Folder] = None, + ): if name is None: extention = file_path.split('.')[-1] name = f'{util.random_name()}.{extention}' diff --git a/test/integration_new/context_managers/box_test_web_link.py b/test/integration_new/context_managers/box_test_web_link.py index cfae6f97f..62fb0c83c 100644 --- a/test/integration_new/context_managers/box_test_web_link.py +++ b/test/integration_new/context_managers/box_test_web_link.py @@ -8,12 +8,16 @@ class BoxTestWebLink: - def __init__(self, *, url: str, name: str = None, parent_folder: Optional[Folder] = None): + def __init__( + self, *, url: str, name: str = None, parent_folder: Optional[Folder] = None + ): if name is None: name = util.random_name() if parent_folder is None: parent_folder = CLIENT.folder('0') - self._web_link: WebLink = parent_folder.create_web_link(target_url=url, name=name) + self._web_link: WebLink = parent_folder.create_web_link( + target_url=url, name=name + ) def __enter__(self) -> WebLink: return self._web_link diff --git a/test/integration_new/object/ai_itest.py b/test/integration_new/object/ai_itest.py index 8b478d4fe..268314b38 100644 --- a/test/integration_new/object/ai_itest.py +++ b/test/integration_new/object/ai_itest.py @@ -11,17 +11,17 @@ @pytest.fixture(scope='module', autouse=True) def parent_folder(): - with BoxTestFolder(name=f'{FOLDER_TESTS_DIRECTORY_NAME} {datetime.now()}') as folder: + with BoxTestFolder( + name=f'{FOLDER_TESTS_DIRECTORY_NAME} {datetime.now()}' + ) as folder: yield folder def test_send_ai_question(parent_folder, small_file_path): with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as file: - items = [{ - 'id': file.id, - 'type': 'file', - 'content': 'The sun raises in the east.' - }] + items = [ + {'id': file.id, 'type': 'file', 'content': 'The sun raises in the east.'} + ] answer = CLIENT.send_ai_question( items=items, prompt='Which direction does the sun raise?', @@ -33,20 +33,21 @@ def test_send_ai_question(parent_folder, small_file_path): def test_send_ai_text_gen(parent_folder, small_file_path): with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as file: - items = [{ - 'id': file.id, - 'type': 'file', - 'content': 'The sun raises in the east.' - }] - dialogue_history = [{ - 'prompt': 'How does the sun rise?', - 'answer': 'The sun raises in the east.', - 'created_at': '2013-12-12T10:53:43-08:00' - }, { - 'prompt': 'How many hours does it take for the sun to rise?', - 'answer': 'It takes 24 hours for the sun to rise.', - 'created_at': '2013-12-12T11:20:43-08:00' - }] + items = [ + {'id': file.id, 'type': 'file', 'content': 'The sun raises in the east.'} + ] + dialogue_history = [ + { + 'prompt': 'How does the sun rise?', + 'answer': 'The sun raises in the east.', + 'created_at': '2013-12-12T10:53:43-08:00', + }, + { + 'prompt': 'How many hours does it take for the sun to rise?', + 'answer': 'It takes 24 hours for the sun to rise.', + 'created_at': '2013-12-12T11:20:43-08:00', + }, + ] answer = CLIENT.send_ai_text_gen( dialogue_history=dialogue_history, items=items, @@ -57,9 +58,6 @@ def test_send_ai_text_gen(parent_folder, small_file_path): def test_get_ai_agent_default_config(): - config = CLIENT.get_ai_agent_default_config( - mode='text_gen', - language='en' - ) + config = CLIENT.get_ai_agent_default_config(mode='text_gen', language='en') assert config['type'] == 'ai_agent_text_gen' assert config['basic_gen']['model'] != '' diff --git a/test/integration_new/object/collaboration_itest.py b/test/integration_new/object/collaboration_itest.py index 3a927fcca..cdd503dce 100644 --- a/test/integration_new/object/collaboration_itest.py +++ b/test/integration_new/object/collaboration_itest.py @@ -16,8 +16,14 @@ def parent_folder(): yield folder -def test_collaboration(parent_folder, small_file_path, other_user,): - with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as test_file: +def test_collaboration( + parent_folder, + small_file_path, + other_user, +): + with BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as test_file: collaboration = test_file.collaborate(other_user, CollaborationRole.VIEWER) try: assert collaboration.item.id == test_file.id @@ -25,7 +31,10 @@ def test_collaboration(parent_folder, small_file_path, other_user,): assert collaboration.role == CollaborationRole.VIEWER updated_expiration_date = '2088-01-01T00:00:00-08:00' - collaboration_update = {'role': CollaborationRole.EDITOR, 'expires_at': updated_expiration_date} + collaboration_update = { + 'role': CollaborationRole.EDITOR, + 'expires_at': updated_expiration_date, + } updated_collaboration = collaboration.update_info(data=collaboration_update) assert updated_collaboration.role == CollaborationRole.EDITOR diff --git a/test/integration_new/object/file_itest.py b/test/integration_new/object/file_itest.py index b11f58750..576c8b9aa 100644 --- a/test/integration_new/object/file_itest.py +++ b/test/integration_new/object/file_itest.py @@ -7,13 +7,16 @@ import pytz from boxsdk import BoxAPIException -from test.integration_new.context_managers.box_retention_policy_assigment import BoxRetentionPolicyAssignment -from test.integration_new.context_managers.box_retention_policy import BoxRetentionPolicy +from test.integration_new.context_managers.box_retention_policy_assigment import ( + BoxRetentionPolicyAssignment, +) +from test.integration_new.context_managers.box_retention_policy import ( + BoxRetentionPolicy, +) from test.integration_new import util from test.integration_new.context_managers.box_test_file import BoxTestFile from test.integration_new.context_managers.box_test_folder import BoxTestFolder - FILE_TESTS_DIRECTORY_NAME = 'file-integration-tests' @@ -81,7 +84,9 @@ def test_get_download_url(test_file, small_file_path): assert downloaded_file.read() == expected_file.read() -def test_update_contents_with_stream(parent_folder, small_file_path, small_file_v2_path): +def test_update_contents_with_stream( + parent_folder, small_file_path, small_file_v2_path +): with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as file: with open(small_file_v2_path, 'rb') as file_v2: updated_file = file.update_contents_with_stream(file_stream=file_v2) @@ -98,7 +103,9 @@ def test_update_contents(parent_folder, small_file_path, small_file_v2_path): assert updated_file.content() == file_v2.read() -def test_lock_and_unlock(parent_folder, small_file_path, small_file_v2_path, other_user, other_client): +def test_lock_and_unlock( + parent_folder, small_file_path, small_file_v2_path, other_user, other_client +): with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as file: file.collaborate(accessible_by=other_user, role='editor') @@ -123,18 +130,28 @@ def test_get_shared_link(parent_folder, small_file_path, other_user, other_clien with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as file: file.collaborate(accessible_by=other_user, role='editor') - shared_link = other_client.file(file.object_id).get_shared_link(allow_edit=True, allow_preview=True, allow_download=True) + shared_link = other_client.file(file.object_id).get_shared_link( + allow_edit=True, allow_preview=True, allow_download=True + ) result_permissions = file.get().shared_link['permissions'] - assert result_permissions == {'can_preview': True, 'can_download': True, 'can_edit': True} + assert result_permissions == { + 'can_preview': True, + 'can_download': True, + 'can_edit': True, + } assert other_client.get_shared_item(shared_link).id == file.id -def test_get_shared_link_download_url(parent_folder, small_file_path, other_user, other_client): +def test_get_shared_link_download_url( + parent_folder, small_file_path, other_user, other_client +): with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as file: file.collaborate(accessible_by=other_user, role='editor') - shared_link_download_url = other_client.file(file.object_id).get_shared_link_download_url() + shared_link_download_url = other_client.file( + file.object_id + ).get_shared_link_download_url() with open(small_file_path, 'rb') as expected_file: with urllib.request.urlopen(shared_link_download_url) as downloaded_file: @@ -164,7 +181,9 @@ def test_and_get_task(parent_folder, small_file_path): def test_get_previous_versions(parent_folder, small_file_path, small_file_v2_path): - with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as test_file: + with BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as test_file: test_file_version = test_file.file_version assert not list(test_file.get_previous_versions()) @@ -180,7 +199,9 @@ def test_get_previous_versions(parent_folder, small_file_path, small_file_v2_pat def test_promote_version(parent_folder, small_file_path, small_file_v2_path): - with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as test_file: + with BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as test_file: test_file_version = test_file.file_version updated_test_file = test_file.update_contents(small_file_v2_path) @@ -188,23 +209,35 @@ def test_promote_version(parent_folder, small_file_path, small_file_v2_path): assert updated_file_version != test_file_version - promoted_file_version = updated_test_file.promote_version(file_version=test_file_version) + promoted_file_version = updated_test_file.promote_version( + file_version=test_file_version + ) - assert test_file.get(fields=('file_version',)).file_version == promoted_file_version - assert set(updated_test_file.get_previous_versions()) == {test_file_version, updated_file_version} + assert ( + test_file.get(fields=('file_version',)).file_version + == promoted_file_version + ) + assert set(updated_test_file.get_previous_versions()) == { + test_file_version, + updated_file_version, + } with open(small_file_path, 'rb') as expected_file_v1: assert expected_file_v1.read() == test_file.content() def test_delete_version(parent_folder, small_file_path, small_file_v2_path): - with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as test_file: + with BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as test_file: updated_test_file = test_file.update_contents(small_file_v2_path) previous_version = list(updated_test_file.get_previous_versions())[0] assert previous_version.trashed_at is None - was_deletion_successs = updated_test_file.delete_version(file_version=previous_version) + was_deletion_successs = updated_test_file.delete_version( + file_version=previous_version + ) assert was_deletion_successs assert list(test_file.get_previous_versions())[0].trashed_at @@ -226,14 +259,18 @@ def test_get_representation_info(parent_folder, image_path): def test_get_thumbnail(parent_folder, image_path): with BoxTestFile(parent_folder=parent_folder, file_path=image_path) as test_file: - thumbnail = test_file.get_thumbnail(extension='jpg', min_width=32, max_width=32, min_height=32, max_height=32) + thumbnail = test_file.get_thumbnail( + extension='jpg', min_width=32, max_width=32, min_height=32, max_height=32 + ) assert thumbnail def test_get_thumbnail_representation(parent_folder, image_path): with BoxTestFile(parent_folder=parent_folder, file_path=image_path) as test_file: - thumbnail_representation = test_file.get_thumbnail_representation(extension='jpg', dimensions='32x32') + thumbnail_representation = test_file.get_thumbnail_representation( + extension='jpg', dimensions='32x32' + ) assert thumbnail_representation @@ -251,18 +288,37 @@ def test_copy(test_file, parent_folder): def test_set_disposition_at(parent_folder, small_file_path): - with BoxRetentionPolicy(disposition_action='permanently_delete', retention_length=1) as retention_policy: - with BoxTestFolder(name=f'{FILE_TESTS_DIRECTORY_NAME} {datetime.now()}') as folder_under_retention: - with BoxRetentionPolicyAssignment(retention_policy=retention_policy, assignee=folder_under_retention): - with BoxTestFile(parent_folder=folder_under_retention, file_path=small_file_path) as file_under_retention: - old_disposition_str = file_under_retention.get(fields=('disposition_at',)).disposition_at + with BoxRetentionPolicy( + disposition_action='permanently_delete', retention_length=1 + ) as retention_policy: + with BoxTestFolder( + name=f'{FILE_TESTS_DIRECTORY_NAME} {datetime.now()}' + ) as folder_under_retention: + with BoxRetentionPolicyAssignment( + retention_policy=retention_policy, assignee=folder_under_retention + ): + with BoxTestFile( + parent_folder=folder_under_retention, file_path=small_file_path + ) as file_under_retention: + old_disposition_str = file_under_retention.get( + fields=('disposition_at',) + ).disposition_at old_disposition_datetime = parser.parse(old_disposition_str) - new_disposition_date = datetime.now().replace(microsecond=0).astimezone(pytz.utc) + timedelta(days=2) + new_disposition_date = datetime.now().replace( + microsecond=0 + ).astimezone(pytz.utc) + timedelta(days=2) file_under_retention.set_disposition_at(new_disposition_date) - updated_disposition_str = file_under_retention.get(fields=('disposition_at',)).disposition_at + updated_disposition_str = file_under_retention.get( + fields=('disposition_at',) + ).disposition_at updated_disposition_datetime = parser.parse(updated_disposition_str) - assert updated_disposition_datetime.astimezone(pytz.utc) == new_disposition_date - assert updated_disposition_datetime.astimezone(pytz.utc) != old_disposition_datetime.astimezone(pytz.utc) + assert ( + updated_disposition_datetime.astimezone(pytz.utc) + == new_disposition_date + ) + assert updated_disposition_datetime.astimezone( + pytz.utc + ) != old_disposition_datetime.astimezone(pytz.utc) diff --git a/test/integration_new/object/folder_itest.py b/test/integration_new/object/folder_itest.py index 8da6d69b7..485d44128 100644 --- a/test/integration_new/object/folder_itest.py +++ b/test/integration_new/object/folder_itest.py @@ -8,7 +8,9 @@ from boxsdk.exception import BoxAPIException from boxsdk.object.collaboration import CollaborationRole -from test.integration_new.context_managers.box_metadata_template import BoxTestMetadataTemplate +from test.integration_new.context_managers.box_metadata_template import ( + BoxTestMetadataTemplate, +) from test.integration_new.context_managers.box_test_group import BoxTestGroup from test.integration_new.context_managers.box_test_web_link import BoxTestWebLink from test.integration_new import CLIENT @@ -21,7 +23,9 @@ @pytest.fixture(scope="module", autouse=True) def parent_folder(): - with BoxTestFolder(name=f'{FOLDER_TESTS_DIRECTORY_NAME} {datetime.now()}') as folder: + with BoxTestFolder( + name=f'{FOLDER_TESTS_DIRECTORY_NAME} {datetime.now()}' + ) as folder: yield folder @@ -42,14 +46,18 @@ def test_manual_chunked_upload(parent_folder, large_file, large_file_name): total_size = os.stat(large_file.path).st_size sha1 = hashlib.sha1() with open(large_file.path, 'rb') as content_stream: - upload_session = parent_folder.create_upload_session(file_size=total_size, file_name=large_file_name) + upload_session = parent_folder.create_upload_session( + file_size=total_size, file_name=large_file_name + ) part_array = [] for part_num in range(upload_session.total_parts): copied_length = 0 chunk = b'' while copied_length < upload_session.part_size: - bytes_read = content_stream.read(upload_session.part_size - copied_length) + bytes_read = content_stream.read( + upload_session.part_size - copied_length + ) if bytes_read is None: continue if len(bytes_read) == 0: @@ -57,11 +65,15 @@ def test_manual_chunked_upload(parent_folder, large_file, large_file_name): chunk += bytes_read copied_length += len(bytes_read) - uploaded_part = upload_session.upload_part_bytes(chunk, part_num * upload_session.part_size, total_size) + uploaded_part = upload_session.upload_part_bytes( + chunk, part_num * upload_session.part_size, total_size + ) part_array.append(uploaded_part) sha1.update(chunk) content_sha1 = sha1.digest() - uploaded_file = upload_session.commit(content_sha1=content_sha1, parts=part_array) + uploaded_file = upload_session.commit( + content_sha1=content_sha1, parts=part_array + ) try: assert uploaded_file.id @@ -72,9 +84,13 @@ def test_manual_chunked_upload(parent_folder, large_file, large_file_name): util.permanently_delete(uploaded_file) -def test_auto_chunked_upload_using_upload_session_urls(parent_folder, large_file, large_file_name): +def test_auto_chunked_upload_using_upload_session_urls( + parent_folder, large_file, large_file_name +): total_size = os.stat(large_file.path).st_size - chunked_uploader = parent_folder.get_chunked_uploader(large_file.path, use_upload_session_urls=True) + chunked_uploader = parent_folder.get_chunked_uploader( + large_file.path, use_upload_session_urls=True + ) uploaded_file = chunked_uploader.start() @@ -87,9 +103,13 @@ def test_auto_chunked_upload_using_upload_session_urls(parent_folder, large_file util.permanently_delete(uploaded_file) -def test_auto_chunked_upload_NOT_using_upload_session_urls(parent_folder, large_file, large_file_name): +def test_auto_chunked_upload_NOT_using_upload_session_urls( + parent_folder, large_file, large_file_name +): total_size = os.stat(large_file.path).st_size - chunked_uploader = parent_folder.get_chunked_uploader(large_file.path, use_upload_session_urls=False) + chunked_uploader = parent_folder.get_chunked_uploader( + large_file.path, use_upload_session_urls=False + ) uploaded_file = chunked_uploader.start() @@ -103,16 +123,20 @@ def test_auto_chunked_upload_NOT_using_upload_session_urls(parent_folder, large_ def test_get_items(parent_folder, small_file_path): - with BoxTestFolder(parent_folder=parent_folder) as subfolder, \ - BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as file, \ - BoxTestWebLink(parent_folder=parent_folder, url='https://box.com') as web_link: + with BoxTestFolder(parent_folder=parent_folder) as subfolder, BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as file, BoxTestWebLink( + parent_folder=parent_folder, url='https://box.com' + ) as web_link: assert set(parent_folder.get_items()) == {subfolder, file, web_link} def test_upload_stream_to_folder(parent_folder, small_file_name, small_file_path): with open(small_file_path, 'rb') as stream_to_be_uploaded: - uploaded_file = parent_folder.upload_stream(file_stream=stream_to_be_uploaded, file_name=small_file_name) + uploaded_file = parent_folder.upload_stream( + file_stream=stream_to_be_uploaded, file_name=small_file_name + ) try: assert uploaded_file.id @@ -122,7 +146,9 @@ def test_upload_stream_to_folder(parent_folder, small_file_name, small_file_path def test_upload_small_file_to_folder(parent_folder, small_file_name, small_file_path): - uploaded_file = parent_folder.upload(file_path=small_file_path, file_name=small_file_name) + uploaded_file = parent_folder.upload( + file_path=small_file_path, file_name=small_file_name + ) try: assert uploaded_file.id assert uploaded_file.parent == parent_folder @@ -131,9 +157,11 @@ def test_upload_small_file_to_folder(parent_folder, small_file_name, small_file_ def test_upload_small_file_to_folder_with_disabled_streaming_file_content( - parent_folder, small_file_name, small_file_path + parent_folder, small_file_name, small_file_path ): - uploaded_file = parent_folder.upload(file_path=small_file_path, file_name=small_file_name, stream_file_content=False) + uploaded_file = parent_folder.upload( + file_path=small_file_path, file_name=small_file_name, stream_file_content=False + ) try: assert uploaded_file.id assert uploaded_file.parent == parent_folder @@ -154,22 +182,31 @@ def test_get_shared_link(parent_folder, other_user, other_client): with BoxTestFolder(parent_folder=parent_folder) as folder: folder.collaborate(accessible_by=other_user, role='editor') - shared_link = other_client.folder(folder.object_id).get_shared_link(allow_preview=True, allow_download=True) + shared_link = other_client.folder(folder.object_id).get_shared_link( + allow_preview=True, allow_download=True + ) result_permissions = folder.get().shared_link['permissions'] - assert result_permissions == {'can_preview': True, 'can_download': True, 'can_edit': False} + assert result_permissions == { + 'can_preview': True, + 'can_download': True, + 'can_edit': False, + } assert other_client.get_shared_item(shared_link).id == folder.id @pytest.mark.parametrize( - 'collaborator', [ + 'collaborator', + [ lazy_fixture('user'), lazy_fixture('group'), - ] + ], ) def test_add_collaborator(parent_folder, collaborator): with BoxTestFolder(parent_folder=parent_folder) as folder: - folder.add_collaborator(collaborator=collaborator, role=CollaborationRole.EDITOR) + folder.add_collaborator( + collaborator=collaborator, role=CollaborationRole.EDITOR + ) assert list(folder.get_collaborations())[0].accessible_by == collaborator @@ -180,11 +217,17 @@ def test_add_collaborator_using_email(parent_folder, user): assert list(folder.get_collaborations())[0].accessible_by == user -def test_invite_collaboratur_using_when_nonexistent_user_email_provided(parent_folder, user): +def test_invite_collaboratur_using_when_nonexistent_user_email_provided( + parent_folder, user +): nonexistent_user_email = 'non-existant-user-email@box.com' with BoxTestFolder(parent_folder=parent_folder) as folder: - folder.add_collaborator(collaborator=nonexistent_user_email, role=CollaborationRole.VIEWER) - assert list(folder.get_collaborations())[0].invite_email == nonexistent_user_email + folder.add_collaborator( + collaborator=nonexistent_user_email, role=CollaborationRole.VIEWER + ) + assert ( + list(folder.get_collaborations())[0].invite_email == nonexistent_user_email + ) def test_create_web_link(parent_folder): @@ -210,8 +253,9 @@ def test_delete_folder(parent_folder): def test_cascade_and_get_metadata_cascade_policies(parent_folder): - with BoxTestMetadataTemplate(display_name="test_template") as metadata_template, \ - BoxTestFolder(parent_folder=parent_folder) as folder: + with BoxTestMetadataTemplate( + display_name="test_template" + ) as metadata_template, BoxTestFolder(parent_folder=parent_folder) as folder: folder.cascade_metadata(metadata_template) policy_applied_to_folder = list(folder.get_metadata_cascade_policies())[0] diff --git a/test/integration_new/object/legal_hold_policy_itest.py b/test/integration_new/object/legal_hold_policy_itest.py index d0a3ae2c7..3ac47e1ff 100644 --- a/test/integration_new/object/legal_hold_policy_itest.py +++ b/test/integration_new/object/legal_hold_policy_itest.py @@ -13,7 +13,7 @@ def test_create_legal_hold_policy(): policy_name=policy_name, description=description, filter_started_at=filter_started_at, - filter_ended_at=filter_ended_at + filter_ended_at=filter_ended_at, ) try: @@ -24,12 +24,13 @@ def test_create_legal_hold_policy(): new_policy_name = 'Test Legal Hold Policy ' + util.random_name() new_policy_description = 'Test Legal Hold Policy Description' - legal_hold_policy.update_info(data={ - 'policy_name': new_policy_name, - 'description': new_policy_description - }) + legal_hold_policy.update_info( + data={'policy_name': new_policy_name, 'description': new_policy_description} + ) - legal_hold_policy = CLIENT.legal_hold_policy(policy_id=legal_hold_policy.object_id).get() + legal_hold_policy = CLIENT.legal_hold_policy( + policy_id=legal_hold_policy.object_id + ).get() assert legal_hold_policy.policy_name == new_policy_name assert legal_hold_policy.description == new_policy_description finally: diff --git a/test/integration_new/object/metadata_template_itest.py b/test/integration_new/object/metadata_template_itest.py index 49c513743..c96f26281 100644 --- a/test/integration_new/object/metadata_template_itest.py +++ b/test/integration_new/object/metadata_template_itest.py @@ -23,7 +23,7 @@ def test_create_metedata_template_with_fields(parent_folder): key='state', description='Which state in USA', options=['CA', 'TX', 'NY'], - hidden=True + hidden=True, ) ] diff --git a/test/integration_new/object/retention_policy_assignement_itest.py b/test/integration_new/object/retention_policy_assignement_itest.py index a52f16274..90c0c2825 100644 --- a/test/integration_new/object/retention_policy_assignement_itest.py +++ b/test/integration_new/object/retention_policy_assignement_itest.py @@ -2,18 +2,25 @@ import pytest from boxsdk import BoxAPIException -from test.integration_new.context_managers.box_retention_policy import BoxRetentionPolicy +from test.integration_new.context_managers.box_retention_policy import ( + BoxRetentionPolicy, +) from test.integration_new.context_managers.box_test_file import BoxTestFile from test.integration_new.context_managers.box_test_folder import BoxTestFolder -from test.integration_new.context_managers.box_metadata_template import BoxTestMetadataTemplate +from test.integration_new.context_managers.box_metadata_template import ( + BoxTestMetadataTemplate, +) - -RETENTION_POLICY_ASSIGNMENT_TESTS_DIRECTORY_NAME = 'retention-policy-assignment-integration-tests' +RETENTION_POLICY_ASSIGNMENT_TESTS_DIRECTORY_NAME = ( + 'retention-policy-assignment-integration-tests' +) @pytest.fixture(scope="module", autouse=True) def parent_folder(): - with BoxTestFolder(name=f'{RETENTION_POLICY_ASSIGNMENT_TESTS_DIRECTORY_NAME} {datetime.now()}') as folder: + with BoxTestFolder( + name=f'{RETENTION_POLICY_ASSIGNMENT_TESTS_DIRECTORY_NAME} {datetime.now()}' + ) as folder: yield folder @@ -24,9 +31,15 @@ def test_file(parent_folder, small_file_path): def test_delete_retention_policy_assignment(parent_folder, small_file_path): - with BoxRetentionPolicy(disposition_action='permanently_delete', retention_length=1) as retention_policy: - with BoxTestFolder(name=f'{RETENTION_POLICY_ASSIGNMENT_TESTS_DIRECTORY_NAME} {datetime.now()}') as folder_under_retention: - retention_policy_assignment = retention_policy.assign(folder_under_retention) + with BoxRetentionPolicy( + disposition_action='permanently_delete', retention_length=1 + ) as retention_policy: + with BoxTestFolder( + name=f'{RETENTION_POLICY_ASSIGNMENT_TESTS_DIRECTORY_NAME} {datetime.now()}' + ) as folder_under_retention: + retention_policy_assignment = retention_policy.assign( + folder_under_retention + ) assignment = retention_policy_assignment.get() assert assignment.id is not None @@ -39,8 +52,12 @@ def test_delete_retention_policy_assignment(parent_folder, small_file_path): def test_retention_policy_assignement_to_metadata_template(): with BoxTestMetadataTemplate(display_name="test_template") as metadata_template: - with BoxRetentionPolicy(disposition_action='permanently_delete', retention_length=1) as retention_policy: - retention_policy_assignment = retention_policy.assign(metadata_template, start_date_field='upload_date') + with BoxRetentionPolicy( + disposition_action='permanently_delete', retention_length=1 + ) as retention_policy: + retention_policy_assignment = retention_policy.assign( + metadata_template, start_date_field='upload_date' + ) assignment = retention_policy_assignment.get() assert assignment.id is not None diff --git a/test/integration_new/object/retention_policy_itest.py b/test/integration_new/object/retention_policy_itest.py index 3fc1b504e..7ad1bb2d1 100644 --- a/test/integration_new/object/retention_policy_itest.py +++ b/test/integration_new/object/retention_policy_itest.py @@ -14,7 +14,7 @@ def test_create_metedata_template_with_fields(): disposition_action=disposition_action, retention_length=retention_length, retention_type=retention_type, - description=policy_description + description=policy_description, ) try: @@ -26,15 +26,14 @@ def test_create_metedata_template_with_fields(): new_policy_name = 'Test Retention Policy ' + util.random_name() new_policy_description = 'Test Retention Policy Updated' - retention_policy.update_info(data={ - 'policy_name': new_policy_name, - 'description': new_policy_description - }) + retention_policy.update_info( + data={'policy_name': new_policy_name, 'description': new_policy_description} + ) - retention_policy = CLIENT.retention_policy(retention_id=retention_policy.object_id).get() + retention_policy = CLIENT.retention_policy( + retention_id=retention_policy.object_id + ).get() assert retention_policy.policy_name == new_policy_name assert retention_policy.description == new_policy_description finally: - retention_policy.update_info(data={ - 'status': 'retired' - }) + retention_policy.update_info(data={'status': 'retired'}) diff --git a/test/integration_new/object/sign_request_itest.py b/test/integration_new/object/sign_request_itest.py index 578c56b20..c0977b3a1 100644 --- a/test/integration_new/object/sign_request_itest.py +++ b/test/integration_new/object/sign_request_itest.py @@ -13,12 +13,16 @@ @pytest.fixture(scope="module", autouse=True) def parent_folder(): - with BoxTestFolder(name=f'{SIGN_REQUEST_TESTS_DIRECTORY_NAME} {datetime.now()}') as folder: + with BoxTestFolder( + name=f'{SIGN_REQUEST_TESTS_DIRECTORY_NAME} {datetime.now()}' + ) as folder: yield folder def test_test_sign_request(parent_folder, small_file_path): - with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as test_file: + with BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as test_file: file = { 'id': test_file.object_id, 'type': test_file.object_type, @@ -35,9 +39,7 @@ def test_test_sign_request(parent_folder, small_file_path): signers = [signer1, signer2] sign_request = CLIENT.create_sign_request_v2( - files=files, - signers=signers, - parent_folder_id=parent_folder.id + files=files, signers=signers, parent_folder_id=parent_folder.id ) try: @@ -57,7 +59,9 @@ def test_test_sign_request(parent_folder, small_file_path): def test_webhook_sign_request(parent_folder, small_file_path): - with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as test_file: + with BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as test_file: file = { 'id': test_file.object_id, 'type': test_file.object_type, @@ -68,7 +72,9 @@ def test_webhook_sign_request(parent_folder, small_file_path): } signers = [signer] - with BoxTestSignRequest(files=files, signers=signers, parent_folder_id=parent_folder.id) as sign_request: + with BoxTestSignRequest( + files=files, signers=signers, parent_folder_id=parent_folder.id + ) as sign_request: webhook_url = 'https://example.com/webhook' new_webhook_url = 'https://example.com/new-webhook' @@ -78,18 +84,26 @@ def test_webhook_sign_request(parent_folder, small_file_path): webhook_file = CLIENT.file(file_id=sign_file['id']) webhook = CLIENT.create_webhook( webhook_file, - ['SIGN_REQUEST.COMPLETED', 'SIGN_REQUEST.DECLINED', 'SIGN_REQUEST.EXPIRED'], - webhook_url + [ + 'SIGN_REQUEST.COMPLETED', + 'SIGN_REQUEST.DECLINED', + 'SIGN_REQUEST.EXPIRED', + ], + webhook_url, ) try: assert webhook.id - assert webhook.triggers == ['SIGN_REQUEST.COMPLETED', 'SIGN_REQUEST.DECLINED', 'SIGN_REQUEST.EXPIRED'] + assert webhook.triggers == [ + 'SIGN_REQUEST.COMPLETED', + 'SIGN_REQUEST.DECLINED', + 'SIGN_REQUEST.EXPIRED', + ] assert webhook.address == webhook_url update_data = { 'address': new_webhook_url, - 'triggers': ['SIGN_REQUEST.COMPLETED'] + 'triggers': ['SIGN_REQUEST.COMPLETED'], } webhook = webhook.update_info(data=update_data) diff --git a/test/integration_new/object/trash_itest.py b/test/integration_new/object/trash_itest.py index 26b9e88e0..410dfa25f 100644 --- a/test/integration_new/object/trash_itest.py +++ b/test/integration_new/object/trash_itest.py @@ -1,7 +1,9 @@ from datetime import datetime import pytest -from boxsdk.pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from boxsdk.pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from boxsdk.pagination.marker_based_object_collection import MarkerBasedObjectCollection from test.integration_new import util @@ -30,7 +32,9 @@ def test_trash_get_items(parent_folder, small_file_path): def test_trash_restore_item(parent_folder, small_file_path): - with BoxTestFile(parent_folder=parent_folder, file_path=small_file_path) as test_file: + with BoxTestFile( + parent_folder=parent_folder, file_path=small_file_path + ) as test_file: test_file.delete() folder_items = parent_folder.get_items() assert test_file.id not in [item.id for item in folder_items] diff --git a/test/integration_new/object/user_itest.py b/test/integration_new/object/user_itest.py index 330fb8e2e..688f55700 100644 --- a/test/integration_new/object/user_itest.py +++ b/test/integration_new/object/user_itest.py @@ -1,3 +1,5 @@ def test_upload_and_delete_avatar(user, image_path): avatar_urls = user.upload_avatar(image_path) - assert 'large' in avatar_urls and 'preview' in avatar_urls and 'small' in avatar_urls + assert ( + 'large' in avatar_urls and 'preview' in avatar_urls and 'small' in avatar_urls + ) diff --git a/test/integration_new/util.py b/test/integration_new/util.py index ca88c6302..a8f7d013d 100644 --- a/test/integration_new/util.py +++ b/test/integration_new/util.py @@ -16,16 +16,22 @@ def permanently_delete(item: BaseItem): warnings.warn( f"Unable to permanently remove {item.type}: {item.id} from trash. Probably this item is under retention.", category=RuntimeWarning, - stacklevel=2 + stacklevel=2, ) def get_current_dir_path() -> str: - return str(Path(os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))))) + return str( + Path(os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))) + ) def get_file_path(file_name: str) -> str: - return str(Path(f"{os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))}/resources/{file_name}")) + return str( + Path( + f"{os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))}/resources/{file_name}" + ) + ) def random_name(): diff --git a/test/unit/auth/test_ccg_auth.py b/test/unit/auth/test_ccg_auth.py index efd3d1869..1007e2f6a 100644 --- a/test/unit/auth/test_ccg_auth.py +++ b/test/unit/auth/test_ccg_auth.py @@ -12,7 +12,6 @@ from boxsdk.auth.ccg_auth import CCGAuth from boxsdk.config import API - USER_SUBJECT_TYPE = 'user' ENTERPRISE_SUBJECT_TYPE = 'enterprise' @@ -32,10 +31,19 @@ def successful_token_response(successful_token_json_response): @pytest.fixture -def unsuccessful_ccg_response(box_datetime, status_code, error_description, include_date_header, error_code): - headers = {'Date': box_datetime.strftime('%a, %d %b %Y %H:%M:%S %Z')} if include_date_header else {} +def unsuccessful_ccg_response( + box_datetime, status_code, error_description, include_date_header, error_code +): + headers = ( + {'Date': box_datetime.strftime('%a, %d %b %Y %H:%M:%S %Z')} + if include_date_header + else {} + ) unsuccessful_response = Mock(headers=headers) - unsuccessful_response.json.return_value = {'error_description': error_description, 'error': error_code} + unsuccessful_response.json.return_value = { + 'error_description': error_description, + 'error': error_code, + } unsuccessful_response.status_code = status_code unsuccessful_response.ok = False return unsuccessful_response @@ -43,14 +51,26 @@ def unsuccessful_ccg_response(box_datetime, status_code, error_description, incl @pytest.fixture() def ccg_auth(client_id, client_secret, ccg_user_id, ccg_enterprise_id) -> CCGAuth: - auth = CCGAuth(client_id=client_id, client_secret=client_secret, user=ccg_user_id, enterprise_id=ccg_enterprise_id) - auth._session.get_retry_after_time = MagicMock(return_value=0) # pylint:disable=protected-access + auth = CCGAuth( + client_id=client_id, + client_secret=client_secret, + user=ccg_user_id, + enterprise_id=ccg_enterprise_id, + ) + auth._session.get_retry_after_time = MagicMock( + return_value=0 + ) # pylint:disable=protected-access return auth @pytest.fixture def expect_auth_retry(status_code, error_description, include_date_header, error_code): - return status_code == 400 and 'exp' in error_description and include_date_header and error_code == 'invalid_grant' + return ( + status_code == 400 + and 'exp' in error_description + and include_date_header + and error_code == 'invalid_grant' + ) @pytest.fixture @@ -59,38 +79,57 @@ def box_datetime(): @pytest.mark.parametrize('enterprise_id', (lazy_fixture('mock_enterprise_id'), None)) -@pytest.mark.parametrize('user, expected_normalized_user_id', - [ - (lazy_fixture('mock_user'), lazy_fixture('mock_user_id')), - (lazy_fixture('mock_user_id'), lazy_fixture('mock_user_id')), - (None, None) - ]) -def test_successful_create_ccg_auth_object_and_normalize_user_id(client_id, client_secret, enterprise_id, user, expected_normalized_user_id): - auth = CCGAuth(client_id=client_id, client_secret=client_secret, enterprise_id=enterprise_id, user=user) - assert auth._user_id == expected_normalized_user_id # pylint:disable=protected-access - - -def test_throw_type_error_when_invalid_user_object_provided(client_id, client_secret, mock_enterprise_id): +@pytest.mark.parametrize( + 'user, expected_normalized_user_id', + [ + (lazy_fixture('mock_user'), lazy_fixture('mock_user_id')), + (lazy_fixture('mock_user_id'), lazy_fixture('mock_user_id')), + (None, None), + ], +) +def test_successful_create_ccg_auth_object_and_normalize_user_id( + client_id, client_secret, enterprise_id, user, expected_normalized_user_id +): + auth = CCGAuth( + client_id=client_id, + client_secret=client_secret, + enterprise_id=enterprise_id, + user=user, + ) + assert ( + auth._user_id == expected_normalized_user_id + ) # pylint:disable=protected-access + + +def test_throw_type_error_when_invalid_user_object_provided( + client_id, client_secret, mock_enterprise_id +): with pytest.raises(TypeError): CCGAuth( client_id=client_id, client_secret=client_secret, enterprise_id=mock_enterprise_id, - user=123123 + user=123123, ) -@pytest.mark.parametrize('ccg_user_id, ccg_enterprise_id, user', - [ - (lazy_fixture('mock_user_id'), None, None), - (None, None, lazy_fixture('mock_user_id')), - (None, None, lazy_fixture('mock_user')) - ]) +@pytest.mark.parametrize( + 'ccg_user_id, ccg_enterprise_id, user', + [ + (lazy_fixture('mock_user_id'), None, None), + (None, None, lazy_fixture('mock_user_id')), + (None, None, lazy_fixture('mock_user')), + ], +) def test_authenticate_user_success(ccg_auth, user, mock_user_id): - with mock.patch.object(CCGAuth, '_fetch_access_token', return_value='new_token') as fetch_access_token_mock: + with mock.patch.object( + CCGAuth, '_fetch_access_token', return_value='new_token' + ) as fetch_access_token_mock: ccg_auth.authenticate_user(user) - fetch_access_token_mock.assert_called_once_with(mock_user_id, USER_SUBJECT_TYPE, None) + fetch_access_token_mock.assert_called_once_with( + mock_user_id, USER_SUBJECT_TYPE, None + ) assert ccg_auth._user_id == mock_user_id # pylint:disable=protected-access @@ -101,26 +140,38 @@ def test_authenticate_user_throws_error_when_user_not_provided(ccg_auth): ccg_auth.authenticate_user(None) -@pytest.mark.parametrize('ccg_user_id, ccg_enterprise_id, enterprise_id', - [ - (None, lazy_fixture('mock_enterprise_id'), None), - (None, None, lazy_fixture('mock_enterprise_id')) - ]) +@pytest.mark.parametrize( + 'ccg_user_id, ccg_enterprise_id, enterprise_id', + [ + (None, lazy_fixture('mock_enterprise_id'), None), + (None, None, lazy_fixture('mock_enterprise_id')), + ], +) def test_authenticate_enterprise(ccg_auth, enterprise_id, mock_enterprise_id): - with mock.patch.object(CCGAuth, '_fetch_access_token', return_value='new_token') as fetch_access_token_mock: + with mock.patch.object( + CCGAuth, '_fetch_access_token', return_value='new_token' + ) as fetch_access_token_mock: ccg_auth.authenticate_instance(enterprise_id) - fetch_access_token_mock.assert_called_once_with(mock_enterprise_id, ENTERPRISE_SUBJECT_TYPE, None) - assert ccg_auth._enterprise_id == mock_enterprise_id # pylint:disable=protected-access + fetch_access_token_mock.assert_called_once_with( + mock_enterprise_id, ENTERPRISE_SUBJECT_TYPE, None + ) + assert ( + ccg_auth._enterprise_id == mock_enterprise_id + ) # pylint:disable=protected-access assert ccg_auth._user_id is None # pylint:disable=protected-access -@pytest.mark.parametrize('ccg_user_id, ccg_enterprise_id, enterprise_id', - [ - (None, None, None), - (None, lazy_fixture('mock_enterprise_id'), 'other_enterprise_300') - ]) -def test_authenticate_user_throws_error_when_enetrprise_not_provided_or_conflicts(ccg_auth, enterprise_id): +@pytest.mark.parametrize( + 'ccg_user_id, ccg_enterprise_id, enterprise_id', + [ + (None, None, None), + (None, lazy_fixture('mock_enterprise_id'), 'other_enterprise_300'), + ], +) +def test_authenticate_user_throws_error_when_enetrprise_not_provided_or_conflicts( + ccg_auth, enterprise_id +): with pytest.raises(ValueError): ccg_auth.authenticate_instance(enterprise_id) @@ -128,19 +179,30 @@ def test_authenticate_user_throws_error_when_enetrprise_not_provided_or_conflict @pytest.mark.parametrize('ccg_user_id', (lazy_fixture('mock_user_id'),)) @pytest.mark.parametrize('ccg_enterprise_id', (lazy_fixture('mock_enterprise_id'),)) @pytest.mark.parametrize('status_code', (400, 401)) -@pytest.mark.parametrize('error_description', ('invalid box_sub_type claim', 'invalid kid', "check the 'exp' claim")) +@pytest.mark.parametrize( + 'error_description', + ('invalid box_sub_type claim', 'invalid kid', "check the 'exp' claim"), +) @pytest.mark.parametrize('error_code', ('invalid_grant', 'bad_request')) @pytest.mark.parametrize('include_date_header', (True, False)) def test_auth_retry_for_invalid_exp_claim( - ccg_auth, - status_code, - expect_auth_retry, - unsuccessful_ccg_response, - box_datetime, - mock_enterprise_id + ccg_auth, + status_code, + expect_auth_retry, + unsuccessful_ccg_response, + box_datetime, + mock_enterprise_id, ): - side_effect = [BoxOAuthException(status_code, network_response=unsuccessful_ccg_response), 'jwt_token'] - with mock.patch.object(CCGAuth, '_fetch_access_token', return_value='new_token', side_effect=side_effect) as fetch_access_token_mock: + side_effect = [ + BoxOAuthException(status_code, network_response=unsuccessful_ccg_response), + 'jwt_token', + ] + with mock.patch.object( + CCGAuth, + '_fetch_access_token', + return_value='new_token', + side_effect=side_effect, + ) as fetch_access_token_mock: if not expect_auth_retry: with pytest.raises(BoxOAuthException): @@ -150,33 +212,47 @@ def test_auth_retry_for_invalid_exp_claim( expected_calls = [call(mock_enterprise_id, 'enterprise', None)] if expect_auth_retry: - expected_calls.append(call(mock_enterprise_id, 'enterprise', box_datetime.replace(microsecond=0, tzinfo=None))) + expected_calls.append( + call( + mock_enterprise_id, + 'enterprise', + box_datetime.replace(microsecond=0, tzinfo=None), + ) + ) assert len(fetch_access_token_mock.mock_calls) == len(expected_calls) fetch_access_token_mock.assert_has_calls(expected_calls) @pytest.mark.parametrize('ccg_user_id', (lazy_fixture('mock_user_id'),)) @pytest.mark.parametrize('ccg_enterprise_id', (lazy_fixture('mock_enterprise_id'),)) -@pytest.mark.parametrize('status_code, error_description, error_code', - [(429, 'Request rate limit exceeded', 'rate_limit_exceeded'), - (500, 'Internal Server Error', 'internal_server_error')]) +@pytest.mark.parametrize( + 'status_code, error_description, error_code', + [ + (429, 'Request rate limit exceeded', 'rate_limit_exceeded'), + (500, 'Internal Server Error', 'internal_server_error'), + ], +) @pytest.mark.parametrize('include_date_header', (False,)) def test_auth_retry_for_rate_limit_and_server_errors( - unsuccessful_ccg_response, - ccg_auth, - status_code, - mock_enterprise_id + unsuccessful_ccg_response, ccg_auth, status_code, mock_enterprise_id ): side_effect = [] expected_calls = [] # Retries multiple times, but less than max retries. Then succeeds when it gets a token. for _ in range(API.MAX_RETRY_ATTEMPTS - 2): - side_effect.append(BoxOAuthException(status_code, network_response=unsuccessful_ccg_response)) + side_effect.append( + BoxOAuthException(status_code, network_response=unsuccessful_ccg_response) + ) expected_calls.append(call(mock_enterprise_id, ENTERPRISE_SUBJECT_TYPE, None)) side_effect.append('jwt_token') expected_calls.append(call(mock_enterprise_id, 'enterprise', None)) - with mock.patch.object(CCGAuth, '_fetch_access_token', return_value='new_token', side_effect=side_effect) as fetch_access_token_mock: + with mock.patch.object( + CCGAuth, + '_fetch_access_token', + return_value='new_token', + side_effect=side_effect, + ) as fetch_access_token_mock: ccg_auth.authenticate_instance(mock_enterprise_id) @@ -186,24 +262,32 @@ def test_auth_retry_for_rate_limit_and_server_errors( @pytest.mark.parametrize('ccg_user_id', (lazy_fixture('mock_user_id'),)) @pytest.mark.parametrize('ccg_enterprise_id', (lazy_fixture('mock_enterprise_id'),)) -@pytest.mark.parametrize('status_code, error_description, error_code', - [(429, 'Request rate limit exceeded', 'rate_limit_exceeded'), - (500, 'Internal Server Error', 'internal_server_error')]) +@pytest.mark.parametrize( + 'status_code, error_description, error_code', + [ + (429, 'Request rate limit exceeded', 'rate_limit_exceeded'), + (500, 'Internal Server Error', 'internal_server_error'), + ], +) @pytest.mark.parametrize('include_date_header', (False,)) def test_auth_max_retries_for_rate_limit_and_server_errors( - unsuccessful_ccg_response, - ccg_auth, - status_code, - mock_enterprise_id + unsuccessful_ccg_response, ccg_auth, status_code, mock_enterprise_id ): side_effect = [] expected_calls = [] # Retries multiple times, but less than max retries. Then succeeds when it gets a token. for _ in range(API.MAX_RETRY_ATTEMPTS + 1): - side_effect.append(BoxOAuthException(status_code, network_response=unsuccessful_ccg_response)) + side_effect.append( + BoxOAuthException(status_code, network_response=unsuccessful_ccg_response) + ) expected_calls.append(call(mock_enterprise_id, ENTERPRISE_SUBJECT_TYPE, None)) - with mock.patch.object(CCGAuth, '_fetch_access_token', return_value='new_token', side_effect=side_effect) as fetch_access_token_mock: + with mock.patch.object( + CCGAuth, + '_fetch_access_token', + return_value='new_token', + side_effect=side_effect, + ) as fetch_access_token_mock: with pytest.raises(BoxOAuthException) as error: ccg_auth.authenticate_instance(mock_enterprise_id) @@ -216,12 +300,14 @@ def test_auth_max_retries_for_rate_limit_and_server_errors( @pytest.mark.parametrize('ccg_user_id', (lazy_fixture('mock_user_id'),)) @pytest.mark.parametrize('ccg_enterprise_id', (lazy_fixture('mock_enterprise_id'),)) def test_extract_token_from_success_response( - successful_token_response, - successful_token_json_response, - ccg_auth, - mock_enterprise_id + successful_token_response, + successful_token_json_response, + ccg_auth, + mock_enterprise_id, ): - with mock.patch.object(CCGAuth, '_execute_token_request', return_value=successful_token_response): + with mock.patch.object( + CCGAuth, '_execute_token_request', return_value=successful_token_response + ): ccg_auth.authenticate_instance(mock_enterprise_id) @@ -254,28 +340,28 @@ def test_refresh_enterprise_authentication_when_client_id_is_not_provided(ccg_au @pytest.mark.parametrize('ccg_user_id', (lazy_fixture('mock_user_id'),)) @pytest.mark.parametrize('ccg_enterprise_id', (lazy_fixture('mock_enterprise_id'),)) -@pytest.mark.parametrize('subject_id, subject_type', - [ - (lazy_fixture('mock_user_id'), USER_SUBJECT_TYPE), - (lazy_fixture('mock_enterprise_id'), ENTERPRISE_SUBJECT_TYPE) - ]) +@pytest.mark.parametrize( + 'subject_id, subject_type', + [ + (lazy_fixture('mock_user_id'), USER_SUBJECT_TYPE), + (lazy_fixture('mock_enterprise_id'), ENTERPRISE_SUBJECT_TYPE), + ], +) def test_send_authentication_request_with_correct_params( - ccg_auth, - client_id, - client_secret, - subject_id, - subject_type + ccg_auth, client_id, client_secret, subject_id, subject_type ): expected_data = { 'grant_type': 'client_credentials', 'client_id': client_id, 'client_secret': client_secret, 'box_subject_id': subject_id, - 'box_subject_type': subject_type + 'box_subject_type': subject_type, } ccg_auth.send_token_request = Mock(return_value=('new_token', None)) # pylint:disable=protected-access ccg_auth._fetch_access_token(subject_id, subject_type, None) - ccg_auth.send_token_request.assert_called_once_with(expected_data, access_token=None, expect_refresh_token=False) + ccg_auth.send_token_request.assert_called_once_with( + expected_data, access_token=None, expect_refresh_token=False + ) diff --git a/test/unit/auth/test_cooperatively_managed_oauth2.py b/test/unit/auth/test_cooperatively_managed_oauth2.py index ea48f9a5d..3fcc047e2 100644 --- a/test/unit/auth/test_cooperatively_managed_oauth2.py +++ b/test/unit/auth/test_cooperatively_managed_oauth2.py @@ -3,7 +3,9 @@ from boxsdk.auth import cooperatively_managed_oauth2 -def test_cooperatively_managed_oauth2_calls_retrieve_tokens_during_refresh(access_token, refresh_token): +def test_cooperatively_managed_oauth2_calls_retrieve_tokens_during_refresh( + access_token, refresh_token +): retrieve_tokens = Mock() oauth2 = cooperatively_managed_oauth2.CooperativelyManagedOAuth2( retrieve_tokens=retrieve_tokens, diff --git a/test/unit/auth/test_jwt_auth.py b/test/unit/auth/test_jwt_auth.py index b1f3319e6..04401a39f 100644 --- a/test/unit/auth/test_jwt_auth.py +++ b/test/unit/auth/test_jwt_auth.py @@ -8,7 +8,10 @@ from unittest.mock import Mock, mock_open, patch, sentinel, call from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, generate_private_key as generate_rsa_private_key +from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, + generate_private_key as generate_rsa_private_key, +) from cryptography.hazmat.primitives import serialization import pytest import pytz @@ -36,7 +39,9 @@ def jwt_key_id(): @pytest.fixture(scope='module') def rsa_private_key_object(): - return generate_rsa_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) + return generate_rsa_private_key( + public_exponent=65537, key_size=4096, backend=default_backend() + ) @pytest.fixture(params=(None, b'strong_password')) @@ -46,7 +51,11 @@ def rsa_passphrase(request): @pytest.fixture def rsa_private_key_bytes(rsa_private_key_object, rsa_passphrase): - encryption = serialization.BestAvailableEncryption(rsa_passphrase) if rsa_passphrase else serialization.NoEncryption() + encryption = ( + serialization.BestAvailableEncryption(rsa_passphrase) + if rsa_passphrase + else serialization.NoEncryption() + ) return rsa_private_key_object.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, @@ -66,9 +75,13 @@ def successful_token_response(successful_token_mock, successful_token_json_respo return successful_token_mock -@pytest.mark.parametrize(('key_file', 'key_data'), [(None, None), ('fake sys path', 'fake key data')]) +@pytest.mark.parametrize( + ('key_file', 'key_data'), [(None, None), ('fake sys path', 'fake key data')] +) @pytest.mark.parametrize('rsa_passphrase', [None]) -def test_jwt_auth_init_raises_type_error_unless_exactly_one_of_rsa_private_key_file_or_data_is_given(key_file, key_data, rsa_private_key_bytes): +def test_jwt_auth_init_raises_type_error_unless_exactly_one_of_rsa_private_key_file_or_data_is_given( + key_file, key_data, rsa_private_key_bytes +): kwargs = dict( rsa_private_key_data=rsa_private_key_bytes, client_id=None, @@ -84,7 +97,9 @@ def test_jwt_auth_init_raises_type_error_unless_exactly_one_of_rsa_private_key_f @pytest.mark.parametrize('key_data', [object(), 'ƒøø']) @pytest.mark.parametrize('rsa_passphrase', [None]) -def test_jwt_auth_init_raises_type_error_if_rsa_private_key_data_has_unexpected_type(key_data, rsa_private_key_bytes): +def test_jwt_auth_init_raises_type_error_if_rsa_private_key_data_has_unexpected_type( + key_data, rsa_private_key_bytes +): kwargs = dict( rsa_private_key_data=rsa_private_key_bytes, client_id=None, @@ -98,8 +113,12 @@ def test_jwt_auth_init_raises_type_error_if_rsa_private_key_data_has_unexpected_ JWTAuth(**kwargs) -@pytest.mark.parametrize('rsa_private_key_data_type', [io.BytesIO, str, bytes, RSAPrivateKey]) -def test_jwt_auth_init_accepts_rsa_private_key_data(rsa_private_key_bytes, rsa_passphrase, rsa_private_key_data_type): +@pytest.mark.parametrize( + 'rsa_private_key_data_type', [io.BytesIO, str, bytes, RSAPrivateKey] +) +def test_jwt_auth_init_accepts_rsa_private_key_data( + rsa_private_key_bytes, rsa_passphrase, rsa_private_key_data_type +): if rsa_private_key_data_type is str: rsa_private_key_data = str(rsa_private_key_bytes.decode('ascii')) elif rsa_private_key_data_type is RSAPrivateKey: @@ -128,13 +147,13 @@ def pass_private_key_by_path(request): @pytest.fixture def jwt_auth_init_mocks( - mock_box_session, - successful_token_response, - jwt_algorithm, - jwt_key_id, - rsa_passphrase, - rsa_private_key_bytes, - pass_private_key_by_path, + mock_box_session, + successful_token_response, + jwt_algorithm, + jwt_key_id, + rsa_passphrase, + rsa_private_key_bytes, + pass_private_key_by_path, ): # pylint:disable=redefined-outer-name @@ -153,20 +172,30 @@ def _jwt_auth_init_mocks(**kwargs): 'box_device_name': 'my_awesome_device', } mock_box_session.request.return_value = successful_token_response - with patch('boxsdk.auth.jwt_auth.open', mock_open(read_data=rsa_private_key_bytes), create=True) as jwt_auth_open: - with patch('cryptography.hazmat.primitives.serialization.load_pem_private_key') as load_pem_private_key: + with patch( + 'boxsdk.auth.jwt_auth.open', + mock_open(read_data=rsa_private_key_bytes), + create=True, + ) as jwt_auth_open: + with patch( + 'cryptography.hazmat.primitives.serialization.load_pem_private_key' + ) as load_pem_private_key: oauth = JWTAuth( client_id=fake_client_id, client_secret=fake_client_secret, - rsa_private_key_file_sys_path=(sentinel.rsa_path if pass_private_key_by_path else None), - rsa_private_key_data=(None if pass_private_key_by_path else rsa_private_key_bytes), + rsa_private_key_file_sys_path=( + sentinel.rsa_path if pass_private_key_by_path else None + ), + rsa_private_key_data=( + None if pass_private_key_by_path else rsa_private_key_bytes + ), rsa_private_key_passphrase=rsa_passphrase, session=mock_box_session, box_device_name='my_awesome_device', jwt_algorithm=jwt_algorithm, jwt_key_id=jwt_key_id, enterprise_id=kwargs.pop('enterprise_id', None), - **kwargs + **kwargs, ) if pass_private_key_by_path: jwt_auth_open.assert_called_once_with(sentinel.rsa_path, 'rb') @@ -189,19 +218,29 @@ def _jwt_auth_init_mocks(**kwargs): headers={'content-type': 'application/x-www-form-urlencoded'}, access_token=None, ) - assert oauth.access_token == successful_token_response.json()['access_token'] + assert ( + oauth.access_token == successful_token_response.json()['access_token'] + ) return _jwt_auth_init_mocks -def test_refresh_authenticates_with_user_if_enterprise_id_and_user_both_passed_to_constructor(jwt_auth_init_and_auth_mocks): +def test_refresh_authenticates_with_user_if_enterprise_id_and_user_both_passed_to_constructor( + jwt_auth_init_and_auth_mocks, +): user = 'fake_user_id' - with jwt_auth_init_and_auth_mocks(sub=user, sub_type='user', enterprise_id='fake_enterprise_id', user=user) as oauth: + with jwt_auth_init_and_auth_mocks( + sub=user, sub_type='user', enterprise_id='fake_enterprise_id', user=user + ) as oauth: oauth.refresh(None) -@pytest.mark.parametrize('jwt_auth_method_name', ['authenticate_user', 'authenticate_instance']) -def test_authenticate_raises_value_error_if_sub_was_never_given(jwt_auth_init_mocks, jwt_auth_method_name): +@pytest.mark.parametrize( + 'jwt_auth_method_name', ['authenticate_user', 'authenticate_instance'] +) +def test_authenticate_raises_value_error_if_sub_was_never_given( + jwt_auth_init_mocks, jwt_auth_method_name +): with jwt_auth_init_mocks(assert_authed=False) as params: auth = params[0] authenticate_method = getattr(auth, jwt_auth_method_name) @@ -209,13 +248,17 @@ def test_authenticate_raises_value_error_if_sub_was_never_given(jwt_auth_init_mo authenticate_method() -def test_jwt_auth_constructor_raises_type_error_if_user_is_unsupported_type(jwt_auth_init_mocks): +def test_jwt_auth_constructor_raises_type_error_if_user_is_unsupported_type( + jwt_auth_init_mocks, +): with pytest.raises(TypeError): with jwt_auth_init_mocks(user=object()): assert False -def test_authenticate_user_raises_type_error_if_user_is_unsupported_type(jwt_auth_init_mocks): +def test_authenticate_user_raises_type_error_if_user_is_unsupported_type( + jwt_auth_init_mocks, +): with jwt_auth_init_mocks(assert_authed=False) as params: auth = params[0] with pytest.raises(TypeError): @@ -223,14 +266,18 @@ def test_authenticate_user_raises_type_error_if_user_is_unsupported_type(jwt_aut @pytest.mark.parametrize('user_id_for_init', [None, 'fake_user_id_1']) -def test_authenticate_user_saves_user_id_for_future_calls(jwt_auth_init_and_auth_mocks, user_id_for_init, jwt_encode): +def test_authenticate_user_saves_user_id_for_future_calls( + jwt_auth_init_and_auth_mocks, user_id_for_init, jwt_encode +): def assert_jwt_encode_call_args(user_id): assert jwt_encode.call_args[0][0]['sub'] == user_id assert jwt_encode.call_args[0][0]['box_sub_type'] == 'user' jwt_encode.call_args = None - with jwt_auth_init_and_auth_mocks(sub=None, sub_type=None, assert_authed=False, user=user_id_for_init) as auth: + with jwt_auth_init_and_auth_mocks( + sub=None, sub_type=None, assert_authed=False, user=user_id_for_init + ) as auth: for new_user_id in ['fake_user_id_2', 'fake_user_id_3']: auth.authenticate_user(new_user_id) assert_jwt_encode_call_args(new_user_id) @@ -238,16 +285,24 @@ def assert_jwt_encode_call_args(user_id): assert_jwt_encode_call_args(new_user_id) -def test_authenticate_instance_raises_value_error_if_different_enterprise_id_is_given(jwt_auth_init_mocks): - with jwt_auth_init_mocks(enterprise_id='fake_enterprise_id_1', assert_authed=False) as params: +def test_authenticate_instance_raises_value_error_if_different_enterprise_id_is_given( + jwt_auth_init_mocks, +): + with jwt_auth_init_mocks( + enterprise_id='fake_enterprise_id_1', assert_authed=False + ) as params: auth = params[0] with pytest.raises(ValueError): auth.authenticate_instance('fake_enterprise_id_2') -def test_authenticate_instance_saves_enterprise_id_for_future_calls(jwt_auth_init_and_auth_mocks): +def test_authenticate_instance_saves_enterprise_id_for_future_calls( + jwt_auth_init_and_auth_mocks, +): enterprise_id = 'fake_enterprise_id' - with jwt_auth_init_and_auth_mocks(sub=enterprise_id, sub_type='enterprise', assert_authed=False) as auth: + with jwt_auth_init_and_auth_mocks( + sub=enterprise_id, sub_type='enterprise', assert_authed=False + ) as auth: auth.authenticate_instance(enterprise_id) auth.authenticate_instance() auth.authenticate_instance(enterprise_id) @@ -265,10 +320,14 @@ def jwt_encode(): def jwt_auth_auth_mocks(jti_length, jwt_algorithm, jwt_key_id, jwt_encode): @contextmanager - def _jwt_auth_auth_mocks(sub, sub_type, oauth, assertion, client_id, secret, assert_authed=True): + def _jwt_auth_auth_mocks( + sub, sub_type, oauth, assertion, client_id, secret, assert_authed=True + ): # pylint:disable=redefined-outer-name with patch('boxsdk.auth.jwt_auth.datetime') as mock_datetime: - with patch('boxsdk.auth.jwt_auth.random.SystemRandom') as mock_system_random: + with patch( + 'boxsdk.auth.jwt_auth.random.SystemRandom' + ) as mock_system_random: jwt_encode.return_value = assertion mock_datetime.utcnow.return_value = datetime(2015, 7, 6, 12, 1, 2) mock_datetime.return_value = datetime(1970, 1, 1) @@ -283,21 +342,28 @@ def _jwt_auth_auth_mocks(sub, sub_type, oauth, assertion, client_id, secret, ass ascii_alphabet = string.ascii_letters + string.digits ascii_len = len(ascii_alphabet) - jti = ''.join(ascii_alphabet[int(r * ascii_len)] for r in random_choices) + jti = ''.join( + ascii_alphabet[int(r * ascii_len)] for r in random_choices + ) yield oauth if assert_authed: system_random.randint.assert_called_once_with(16, 128) assert len(system_random.random.mock_calls) == jti_length - jwt_encode.assert_called_once_with({ - 'iss': client_id, - 'sub': sub, - 'box_sub_type': sub_type, - 'aud': 'https://api.box.com/oauth2/token', - 'jti': jti, - 'exp': exp, - }, secret, algorithm=jwt_algorithm, headers={'kid': jwt_key_id}) + jwt_encode.assert_called_once_with( + { + 'iss': client_id, + 'sub': sub, + 'box_sub_type': sub_type, + 'aud': 'https://api.box.com/oauth2/token', + 'jti': jti, + 'exp': exp, + }, + secret, + algorithm=jwt_algorithm, + headers={'kid': jwt_key_id}, + ) return _jwt_auth_auth_mocks @@ -306,10 +372,18 @@ def _jwt_auth_auth_mocks(sub, sub_type, oauth, assertion, client_id, secret, ass def jwt_auth_init_and_auth_mocks(jwt_auth_init_mocks, jwt_auth_auth_mocks): @contextmanager - def _jwt_auth_init_and_auth_mocks(sub, sub_type, *jwt_auth_init_mocks_args, **jwt_auth_init_mocks_kwargs): + def _jwt_auth_init_and_auth_mocks( + sub, sub_type, *jwt_auth_init_mocks_args, **jwt_auth_init_mocks_kwargs + ): assert_authed = jwt_auth_init_mocks_kwargs.pop('assert_authed', True) - with jwt_auth_init_mocks(*jwt_auth_init_mocks_args, assert_authed=assert_authed, **jwt_auth_init_mocks_kwargs) as params: - with jwt_auth_auth_mocks(sub, sub_type, *params, assert_authed=assert_authed) as oauth: + with jwt_auth_init_mocks( + *jwt_auth_init_mocks_args, + assert_authed=assert_authed, + **jwt_auth_init_mocks_kwargs, + ) as params: + with jwt_auth_auth_mocks( + sub, sub_type, *params, assert_authed=assert_authed + ) as oauth: yield oauth return _jwt_auth_init_and_auth_mocks @@ -319,7 +393,9 @@ def _jwt_auth_init_and_auth_mocks(sub, sub_type, *jwt_auth_init_mocks_args, **jw ('user', 'pass_in_init'), list(product([str('fake_user_id'), User(None, 'fake_user_id')], [False, True])), ) -def test_authenticate_user_sends_post_request_with_correct_params(jwt_auth_init_and_auth_mocks, user, pass_in_init): +def test_authenticate_user_sends_post_request_with_correct_params( + jwt_auth_init_and_auth_mocks, user, pass_in_init +): # pylint:disable=redefined-outer-name if isinstance(user, User): user_id = user.object_id @@ -337,8 +413,12 @@ def test_authenticate_user_sends_post_request_with_correct_params(jwt_auth_init_ oauth.authenticate_user(*authenticate_params) -@pytest.mark.parametrize(('pass_in_init', 'pass_in_auth'), [(True, False), (False, True), (True, True)]) -def test_authenticate_instance_sends_post_request_with_correct_params(jwt_auth_init_and_auth_mocks, pass_in_init, pass_in_auth): +@pytest.mark.parametrize( + ('pass_in_init', 'pass_in_auth'), [(True, False), (False, True), (True, True)] +) +def test_authenticate_instance_sends_post_request_with_correct_params( + jwt_auth_init_and_auth_mocks, pass_in_init, pass_in_auth +): # pylint:disable=redefined-outer-name enterprise_id = 'fake_enterprise_id' init_kwargs = {} @@ -347,21 +427,29 @@ def test_authenticate_instance_sends_post_request_with_correct_params(jwt_auth_i init_kwargs['enterprise_id'] = enterprise_id if pass_in_auth: auth_params.append(enterprise_id) - with jwt_auth_init_and_auth_mocks(enterprise_id, 'enterprise', **init_kwargs) as oauth: + with jwt_auth_init_and_auth_mocks( + enterprise_id, 'enterprise', **init_kwargs + ) as oauth: oauth.authenticate_instance(*auth_params) -def test_refresh_app_user_sends_post_request_with_correct_params(jwt_auth_init_and_auth_mocks): +def test_refresh_app_user_sends_post_request_with_correct_params( + jwt_auth_init_and_auth_mocks, +): # pylint:disable=redefined-outer-name fake_user_id = 'fake_user_id' with jwt_auth_init_and_auth_mocks(fake_user_id, 'user', user=fake_user_id) as oauth: oauth.refresh(None) -def test_refresh_instance_sends_post_request_with_correct_params(jwt_auth_init_and_auth_mocks): +def test_refresh_instance_sends_post_request_with_correct_params( + jwt_auth_init_and_auth_mocks, +): # pylint:disable=redefined-outer-name enterprise_id = 'fake_enterprise_id' - with jwt_auth_init_and_auth_mocks(enterprise_id, 'enterprise', enterprise_id=enterprise_id) as oauth: + with jwt_auth_init_and_auth_mocks( + enterprise_id, 'enterprise', enterprise_id=enterprise_id + ) as oauth: oauth.refresh(None) @@ -392,12 +480,12 @@ def fake_enterprise_id(): @pytest.fixture def app_config_json_content( - fake_client_id, - fake_client_secret, - fake_enterprise_id, - jwt_key_id, - rsa_private_key_bytes, - rsa_passphrase, + fake_client_id, + fake_client_secret, + fake_enterprise_id, + jwt_key_id, + rsa_private_key_bytes, + rsa_passphrase, ): template = r""" {{ @@ -424,12 +512,12 @@ def app_config_json_content( @pytest.fixture() def assert_jwt_kwargs_expected( - fake_client_id, - fake_client_secret, - fake_enterprise_id, - jwt_key_id, - rsa_private_key_bytes, - rsa_passphrase, + fake_client_id, + fake_client_secret, + fake_enterprise_id, + jwt_key_id, + rsa_private_key_bytes, + rsa_passphrase, ): def _assert_jwt_kwargs_expected(jwt_auth): assert jwt_auth.kwargs['client_id'] == fake_client_id @@ -437,34 +525,53 @@ def _assert_jwt_kwargs_expected(jwt_auth): assert jwt_auth.kwargs['enterprise_id'] == fake_enterprise_id assert jwt_auth.kwargs['jwt_key_id'] == jwt_key_id assert jwt_auth.kwargs['rsa_private_key_data'] == rsa_private_key_bytes.decode() - assert jwt_auth.kwargs['rsa_private_key_passphrase'] == (rsa_passphrase and rsa_passphrase.decode()) + assert jwt_auth.kwargs['rsa_private_key_passphrase'] == ( + rsa_passphrase and rsa_passphrase.decode() + ) return _assert_jwt_kwargs_expected def test_from_config_file( - jwt_subclass_that_just_stores_params, - app_config_json_content, - assert_jwt_kwargs_expected, + jwt_subclass_that_just_stores_params, + app_config_json_content, + assert_jwt_kwargs_expected, ): # pylint:disable=redefined-outer-name - with patch('boxsdk.auth.jwt_auth.open', mock_open(read_data=app_config_json_content), create=True): - jwt_auth_from_config_file = jwt_subclass_that_just_stores_params.from_settings_file('fake_config_file_sys_path') + with patch( + 'boxsdk.auth.jwt_auth.open', + mock_open(read_data=app_config_json_content), + create=True, + ): + jwt_auth_from_config_file = ( + jwt_subclass_that_just_stores_params.from_settings_file( + 'fake_config_file_sys_path' + ) + ) assert_jwt_kwargs_expected(jwt_auth_from_config_file) def test_from_settings_dictionary( - jwt_subclass_that_just_stores_params, - app_config_json_content, - assert_jwt_kwargs_expected, + jwt_subclass_that_just_stores_params, + app_config_json_content, + assert_jwt_kwargs_expected, ): - jwt_auth_from_dictionary = jwt_subclass_that_just_stores_params.from_settings_dictionary(json.loads(app_config_json_content)) + jwt_auth_from_dictionary = ( + jwt_subclass_that_just_stores_params.from_settings_dictionary( + json.loads(app_config_json_content) + ) + ) assert_jwt_kwargs_expected(jwt_auth_from_dictionary) @pytest.fixture def expect_auth_retry(status_code, error_description, include_date_header, error_code): - return status_code == 400 and ('exp' in error_description or 'jti' in error_description) and include_date_header and error_code == 'invalid_grant' + return ( + status_code == 400 + and ('exp' in error_description or 'jti' in error_description) + and include_date_header + and error_code == 'invalid_grant' + ) @pytest.fixture @@ -473,10 +580,19 @@ def box_datetime(): @pytest.fixture -def unsuccessful_jwt_response(box_datetime, status_code, error_description, include_date_header, error_code): - headers = {'Date': box_datetime.strftime('%a, %d %b %Y %H:%M:%S %Z')} if include_date_header else {} +def unsuccessful_jwt_response( + box_datetime, status_code, error_description, include_date_header, error_code +): + headers = ( + {'Date': box_datetime.strftime('%a, %d %b %Y %H:%M:%S %Z')} + if include_date_header + else {} + ) unsuccessful_response = Mock(headers=headers) - unsuccessful_response.json.return_value = {'error_description': error_description, 'error': error_code} + unsuccessful_response.json.return_value = { + 'error_description': error_description, + 'error': error_code, + } unsuccessful_response.status_code = status_code unsuccessful_response.ok = False return unsuccessful_response @@ -486,22 +602,32 @@ def unsuccessful_jwt_response(box_datetime, status_code, error_description, incl @pytest.mark.parametrize('rsa_passphrase', (None,)) @pytest.mark.parametrize('pass_private_key_by_path', (False,)) @pytest.mark.parametrize('status_code', (400, 401)) -@pytest.mark.parametrize('error_description', ( - 'invalid box_sub_type claim', 'invalid kid', "check the 'exp' claim", "check the 'jti' claim")) +@pytest.mark.parametrize( + 'error_description', + ( + 'invalid box_sub_type claim', + 'invalid kid', + "check the 'exp' claim", + "check the 'jti' claim", + ), +) @pytest.mark.parametrize('error_code', ('invalid_grant', 'bad_request')) @pytest.mark.parametrize('include_date_header', (True, False)) def test_auth_retry_for_invalid_exp_claim( - jwt_auth_init_mocks, - expect_auth_retry, - unsuccessful_jwt_response, - box_datetime, + jwt_auth_init_mocks, + expect_auth_retry, + unsuccessful_jwt_response, + box_datetime, ): # pylint:disable=redefined-outer-name enterprise_id = 'fake_enterprise_id' with jwt_auth_init_mocks(assert_authed=False) as params: auth = params[0] with patch.object(auth, '_fetch_access_token') as mock_send_jwt: - mock_send_jwt.side_effect = [BoxOAuthException(400, network_response=unsuccessful_jwt_response), 'jwt_token'] + mock_send_jwt.side_effect = [ + BoxOAuthException(400, network_response=unsuccessful_jwt_response), + 'jwt_token', + ] if not expect_auth_retry: with pytest.raises(BoxOAuthException): auth.authenticate_instance(enterprise_id) @@ -509,7 +635,13 @@ def test_auth_retry_for_invalid_exp_claim( auth.authenticate_instance(enterprise_id) expected_calls = [call(enterprise_id, 'enterprise', None)] if expect_auth_retry: - expected_calls.append(call(enterprise_id, 'enterprise', box_datetime.replace(microsecond=0, tzinfo=None))) + expected_calls.append( + call( + enterprise_id, + 'enterprise', + box_datetime.replace(microsecond=0, tzinfo=None), + ) + ) assert len(mock_send_jwt.mock_calls) == len(expected_calls) mock_send_jwt.assert_has_calls(expected_calls) @@ -522,8 +654,8 @@ def test_auth_retry_for_invalid_exp_claim( @pytest.mark.parametrize('error_code', ('rate_limit_exceeded',)) @pytest.mark.parametrize('include_date_header', (False,)) def test_auth_retry_for_rate_limit_error( - jwt_auth_init_mocks, - unsuccessful_jwt_response, + jwt_auth_init_mocks, + unsuccessful_jwt_response, ): # pylint:disable=redefined-outer-name enterprise_id = 'fake_enterprise_id' @@ -534,7 +666,9 @@ def test_auth_retry_for_rate_limit_error( expected_calls = [] # Retries multiple times, but less than max retries. Then succeeds when it gets a token. for _ in range(API.MAX_RETRY_ATTEMPTS - 2): - side_effect.append(BoxOAuthException(429, network_response=unsuccessful_jwt_response)) + side_effect.append( + BoxOAuthException(429, network_response=unsuccessful_jwt_response) + ) expected_calls.append(call(enterprise_id, 'enterprise', None)) side_effect.append('jwt_token') expected_calls.append(call(enterprise_id, 'enterprise', None)) @@ -553,8 +687,8 @@ def test_auth_retry_for_rate_limit_error( @pytest.mark.parametrize('error_code', ('rate_limit_exceeded',)) @pytest.mark.parametrize('include_date_header', (False,)) def test_auth_max_retries_for_rate_limit_error( - jwt_auth_init_mocks, - unsuccessful_jwt_response, + jwt_auth_init_mocks, + unsuccessful_jwt_response, ): # pylint:disable=redefined-outer-name enterprise_id = 'fake_enterprise_id' @@ -565,7 +699,9 @@ def test_auth_max_retries_for_rate_limit_error( expected_calls = [] # Retries max number of times, then throws the error for _ in range(API.MAX_RETRY_ATTEMPTS + 1): - side_effect.append(BoxOAuthException(429, network_response=unsuccessful_jwt_response)) + side_effect.append( + BoxOAuthException(429, network_response=unsuccessful_jwt_response) + ) expected_calls.append(call(enterprise_id, 'enterprise', None)) mock_send_jwt.side_effect = side_effect @@ -584,8 +720,8 @@ def test_auth_max_retries_for_rate_limit_error( @pytest.mark.parametrize('error_code', ('internal_server_error',)) @pytest.mark.parametrize('include_date_header', (False,)) def test_auth_retry_for_internal_server_error( - jwt_auth_init_mocks, - unsuccessful_jwt_response, + jwt_auth_init_mocks, + unsuccessful_jwt_response, ): # pylint:disable=redefined-outer-name enterprise_id = 'fake_enterprise_id' @@ -596,7 +732,9 @@ def test_auth_retry_for_internal_server_error( expected_calls = [] # Retries multiple times, but less than max retries. Then succeeds when it gets a token. for _ in range(API.MAX_RETRY_ATTEMPTS - 2): - side_effect.append(BoxOAuthException(500, network_response=unsuccessful_jwt_response)) + side_effect.append( + BoxOAuthException(500, network_response=unsuccessful_jwt_response) + ) expected_calls.append(call(enterprise_id, 'enterprise', None)) side_effect.append('jwt_token') expected_calls.append(call(enterprise_id, 'enterprise', None)) @@ -615,8 +753,8 @@ def test_auth_retry_for_internal_server_error( @pytest.mark.parametrize('error_code', ('internal_server_error',)) @pytest.mark.parametrize('include_date_header', (False,)) def test_auth_max_retries_for_internal_server_error( - jwt_auth_init_mocks, - unsuccessful_jwt_response, + jwt_auth_init_mocks, + unsuccessful_jwt_response, ): # pylint:disable=redefined-outer-name enterprise_id = 'fake_enterprise_id' @@ -627,7 +765,9 @@ def test_auth_max_retries_for_internal_server_error( expected_calls = [] # Retries max number of times, then throws the error for _ in range(API.MAX_RETRY_ATTEMPTS + 1): - side_effect.append(BoxOAuthException(500, network_response=unsuccessful_jwt_response)) + side_effect.append( + BoxOAuthException(500, network_response=unsuccessful_jwt_response) + ) expected_calls.append(call(enterprise_id, 'enterprise', None)) mock_send_jwt.side_effect = side_effect diff --git a/test/unit/auth/test_oauth2.py b/test/unit/auth/test_oauth2.py index f5f6e17da..4c10ed88f 100644 --- a/test/unit/auth/test_oauth2.py +++ b/test/unit/auth/test_oauth2.py @@ -21,7 +21,9 @@ class MyBaseException(BaseException): pass -@pytest.fixture(params=('https://url.com/foo?bar=baz', 'https://ȕŕľ.com/ƒőő?Ƅȁŕ=Ƅȁż', None)) +@pytest.fixture( + params=('https://url.com/foo?bar=baz', 'https://ȕŕľ.com/ƒőő?Ƅȁŕ=Ƅȁż', None) +) def redirect_url(request): """A value for the `redirect_uri` query string parameter for OAuth2.""" return request.param @@ -48,7 +50,9 @@ def test_get_correct_authorization_url(redirect_url): assert re.match('^box_csrf_token_[A-Za-z0-9]{16}$', csrf_token) -def test_authenticate_send_post_request_with_correct_params(mock_box_session, successful_token_response): +def test_authenticate_send_post_request_with_correct_params( + mock_box_session, successful_token_response +): fake_client_id = 'fake_client_id' fake_client_secret = 'fake_client_secret' fake_auth_code = 'fake_auth_code' @@ -83,9 +87,9 @@ def test_authenticate_send_post_request_with_correct_params(mock_box_session, su @pytest.mark.parametrize('_', range(10)) def test_refresh_send_post_request_with_correct_params_and_handles_multiple_requests( - mock_box_session, - successful_token_response, - _, + mock_box_session, + successful_token_response, + _, ): fake_client_id = 'fake_client_id' fake_client_secret = 'fake_client_secret' @@ -130,7 +134,9 @@ def test_refresh_send_post_request_with_correct_params_and_handles_multiple_requ ) -def test_authenticate_stores_tokens_correctly(mock_box_session, successful_token_response): +def test_authenticate_stores_tokens_correctly( + mock_box_session, successful_token_response +): fake_client_id = 'fake_client_id' fake_client_secret = 'fake_client_secret' fake_auth_code = 'fake_auth_code' @@ -153,10 +159,10 @@ def test_authenticate_stores_tokens_correctly(mock_box_session, successful_token @pytest.mark.parametrize('_', range(10)) def test_refresh_gives_back_the_correct_response_and_handles_multiple_requests( - mock_box_session, - successful_token_response, - network_response_with_missing_tokens, - _, + mock_box_session, + successful_token_response, + network_response_with_missing_tokens, + _, ): # pylint:disable=redefined-outer-name fake_client_id = 'fake_client_id' @@ -166,7 +172,10 @@ def test_refresh_gives_back_the_correct_response_and_handles_multiple_requests( # Setup the network layer so that if oauth makes more than one request, it will get a malformed response and failed # the test. - mock_box_session.request.side_effect = [successful_token_response, network_response_with_missing_tokens] + mock_box_session.request.side_effect = [ + successful_token_response, + network_response_with_missing_tokens, + ] oauth = OAuth2( client_id=fake_client_id, client_secret=fake_client_secret, @@ -194,7 +203,7 @@ def refresh_tokens_and_verify_the_response(): @pytest.fixture() def token_method(request): - """ Fixture that returns a partial method based on the method provided in request.param""" + """Fixture that returns a partial method based on the method provided in request.param""" if request.param == OAuth2.refresh: return partial(OAuth2.refresh, access_token_to_refresh='fake_access_token') if request.param == OAuth2.authenticate: @@ -208,9 +217,9 @@ def token_method(request): indirect=True, ) def test_token_request_raises_box_oauth_exception_when_getting_bad_network_response( - token_method, - mock_box_session, - bad_network_response, + token_method, + mock_box_session, + bad_network_response, ): with pytest.raises(BoxOAuthException): mock_box_session.request.return_value = bad_network_response @@ -229,9 +238,9 @@ def test_token_request_raises_box_oauth_exception_when_getting_bad_network_respo indirect=True, ) def test_token_request_raises_box_oauth_exception_when_no_json_object_can_be_decoded( - token_method, - mock_box_session, - non_json_response, + token_method, + mock_box_session, + non_json_response, ): mock_box_session.request.return_value = non_json_response oauth = OAuth2( @@ -244,11 +253,13 @@ def test_token_request_raises_box_oauth_exception_when_no_json_object_can_be_dec token_method(oauth) -@pytest.fixture(params=[ - ['access_token'], - ['refresh_token'], - [], -]) +@pytest.fixture( + params=[ + ['access_token'], + ['refresh_token'], + [], + ] +) def network_response_with_missing_tokens(request): mock_network_response = Mock(DefaultNetworkResponse) mock_network_response.ok = True @@ -259,14 +270,17 @@ def network_response_with_missing_tokens(request): return mock_network_response -@pytest.mark.parametrize('test_method', [ - partial(OAuth2.refresh, access_token_to_refresh='fake_access_token'), - partial(OAuth2.authenticate, auth_code='fake_code') -]) +@pytest.mark.parametrize( + 'test_method', + [ + partial(OAuth2.refresh, access_token_to_refresh='fake_access_token'), + partial(OAuth2.authenticate, auth_code='fake_code'), + ], +) def test_token_request_raises_box_oauth_exception_when_tokens_are_not_in_the_response( - test_method, - mock_box_session, - network_response_with_missing_tokens, + test_method, + mock_box_session, + network_response_with_missing_tokens, ): # pylint:disable=redefined-outer-name mock_box_session.request.return_value = network_response_with_missing_tokens @@ -280,7 +294,9 @@ def test_token_request_raises_box_oauth_exception_when_tokens_are_not_in_the_res test_method(oauth) -def test_oauth_exception_error_and_error_description(mock_box_session, bad_network_response_400): +def test_oauth_exception_error_and_error_description( + mock_box_session, bad_network_response_400 +): mock_box_session.request.return_value = bad_network_response_400 oauth = OAuth2( client_id='', @@ -325,16 +341,16 @@ def oauth(client_id, client_secret, access_token, refresh_token, mock_box_sessio 'access_token,refresh_token,expected_token_to_revoke', ( ('fake_access_token', 'fake_refresh_token', 'fake_access_token'), - (None, 'fake_refresh_token', 'fake_refresh_token') - ) + (None, 'fake_refresh_token', 'fake_refresh_token'), + ), ) def test_revoke_sends_revoke_request( - client_id, - client_secret, - mock_box_session, - access_token, - oauth, - expected_token_to_revoke, + client_id, + client_secret, + mock_box_session, + access_token, + oauth, + expected_token_to_revoke, ): mock_network_response = Mock() mock_network_response.ok = True @@ -353,7 +369,14 @@ def test_revoke_sends_revoke_request( assert oauth.access_token is None -def test_tokens_get_updated_after_noop_refresh(client_id, client_secret, access_token, new_access_token, refresh_token, mock_box_session): +def test_tokens_get_updated_after_noop_refresh( + client_id, + client_secret, + access_token, + new_access_token, + refresh_token, + mock_box_session, +): """`OAuth2` object should update its state with new tokens, after no-op refresh. If the protected method `_get_tokens()` returns new tokens, refresh is @@ -392,26 +415,36 @@ def _get_tokens(self): def test_closed_is_false_after_init(client_id, client_secret, mock_box_session): - auth = OAuth2(client_id=client_id, client_secret=client_secret, session=mock_box_session) + auth = OAuth2( + client_id=client_id, client_secret=client_secret, session=mock_box_session + ) assert auth.closed is False def test_closed_is_true_after_close(client_id, client_secret, mock_box_session): - auth = OAuth2(client_id=client_id, client_secret=client_secret, session=mock_box_session) + auth = OAuth2( + client_id=client_id, client_secret=client_secret, session=mock_box_session + ) auth.close() assert auth.closed is True def test_token_requests_fail_after_close(client_id, client_secret, mock_box_session): - auth = OAuth2(client_id=client_id, client_secret=client_secret, session=mock_box_session) + auth = OAuth2( + client_id=client_id, client_secret=client_secret, session=mock_box_session + ) auth.close() with pytest.raises(ValueError): auth.refresh(auth.access_token) @pytest.mark.parametrize('raise_exception', [False, True]) -def test_context_manager_closes_auth_object(client_id, client_secret, mock_box_session, raise_exception): - auth = OAuth2(client_id=client_id, client_secret=client_secret, session=mock_box_session) +def test_context_manager_closes_auth_object( + client_id, client_secret, mock_box_session, raise_exception +): + auth = OAuth2( + client_id=client_id, client_secret=client_secret, session=mock_box_session + ) try: with auth.closing(): if raise_exception: @@ -422,7 +455,9 @@ def test_context_manager_closes_auth_object(client_id, client_secret, mock_box_s def test_context_manager_fails_after_close(client_id, client_secret, mock_box_session): - auth = OAuth2(client_id=client_id, client_secret=client_secret, session=mock_box_session) + auth = OAuth2( + client_id=client_id, client_secret=client_secret, session=mock_box_session + ) with auth.closing(): pass with pytest.raises(ValueError): @@ -430,40 +465,76 @@ def test_context_manager_fails_after_close(client_id, client_secret, mock_box_se assert False -@pytest.mark.parametrize(('close_args', 'close_kwargs'), [((), {}), ((True,), {}), ((), dict(revoke=True))]) -def test_revoke_on_close(client_id, client_secret, access_token, mock_box_session, close_args, close_kwargs): - auth = OAuth2(client_id=client_id, client_secret=client_secret, access_token=access_token, session=mock_box_session) +@pytest.mark.parametrize( + ('close_args', 'close_kwargs'), [((), {}), ((True,), {}), ((), dict(revoke=True))] +) +def test_revoke_on_close( + client_id, client_secret, access_token, mock_box_session, close_args, close_kwargs +): + auth = OAuth2( + client_id=client_id, + client_secret=client_secret, + access_token=access_token, + session=mock_box_session, + ) with patch.object(auth, 'revoke') as mock_revoke: auth.close(*close_args, **close_kwargs) mock_revoke.assert_called_once_with() -def test_auth_object_is_closed_even_if_revoke_fails(client_id, client_secret, access_token, mock_box_session): - auth = OAuth2(client_id=client_id, client_secret=client_secret, access_token=access_token, session=mock_box_session) +def test_auth_object_is_closed_even_if_revoke_fails( + client_id, client_secret, access_token, mock_box_session +): + auth = OAuth2( + client_id=client_id, + client_secret=client_secret, + access_token=access_token, + session=mock_box_session, + ) with patch.object(auth, 'revoke', side_effect=BoxOAuthException(status=500)): with pytest.raises(BoxOAuthException): auth.close(revoke=True) assert auth.closed is True -@pytest.mark.parametrize(('close_args', 'close_kwargs'), [((False,), {}), ((), dict(revoke=False))]) -def test_revoke_on_close_can_be_skipped(client_id, client_secret, access_token, mock_box_session, close_args, close_kwargs): - auth = OAuth2(client_id=client_id, client_secret=client_secret, access_token=access_token, session=mock_box_session) +@pytest.mark.parametrize( + ('close_args', 'close_kwargs'), [((False,), {}), ((), dict(revoke=False))] +) +def test_revoke_on_close_can_be_skipped( + client_id, client_secret, access_token, mock_box_session, close_args, close_kwargs +): + auth = OAuth2( + client_id=client_id, + client_secret=client_secret, + access_token=access_token, + session=mock_box_session, + ) with patch.object(auth, 'revoke') as mock_revoke: auth.close(*close_args, **close_kwargs) mock_revoke.assert_not_called() -@pytest.mark.parametrize(('raise_from_block', 'raise_from_close', 'expected_exception'), [ - (MyError, None, MyError), - (None, BoxOAuthException(status=500), BoxOAuthException), - (MyError, BoxOAuthException(status=500), MyError), -]) +@pytest.mark.parametrize( + ('raise_from_block', 'raise_from_close', 'expected_exception'), + [ + (MyError, None, MyError), + (None, BoxOAuthException(status=500), BoxOAuthException), + (MyError, BoxOAuthException(status=500), MyError), + ], +) @pytest.mark.parametrize('close_kwargs', [{}, dict(revoke=False), dict(revoke=True)]) def test_context_manager_reraises_first_exception_after_close( - client_id, client_secret, mock_box_session, close_kwargs, raise_from_block, raise_from_close, expected_exception, + client_id, + client_secret, + mock_box_session, + close_kwargs, + raise_from_block, + raise_from_close, + expected_exception, ): - auth = OAuth2(client_id=client_id, client_secret=client_secret, session=mock_box_session) + auth = OAuth2( + client_id=client_id, client_secret=client_secret, session=mock_box_session + ) with patch.object(auth, 'close', side_effect=raise_from_close) as mock_close: with pytest.raises(expected_exception): with auth.closing(**close_kwargs): @@ -473,8 +544,12 @@ def test_context_manager_reraises_first_exception_after_close( @pytest.mark.parametrize('close_kwargs', [{}, dict(revoke=False), dict(revoke=True)]) -def test_context_manager_skips_revoke_on_base_exception(client_id, client_secret, mock_box_session, close_kwargs): - auth = OAuth2(client_id=client_id, client_secret=client_secret, session=mock_box_session) +def test_context_manager_skips_revoke_on_base_exception( + client_id, client_secret, mock_box_session, close_kwargs +): + auth = OAuth2( + client_id=client_id, client_secret=client_secret, session=mock_box_session + ) with patch.object(auth, 'close') as mock_close: with pytest.raises(MyBaseException): with auth.closing(**close_kwargs): diff --git a/test/unit/auth/test_redis_managed_oauth2.py b/test/unit/auth/test_redis_managed_oauth2.py index 05b65b4bf..42f623094 100644 --- a/test/unit/auth/test_redis_managed_oauth2.py +++ b/test/unit/auth/test_redis_managed_oauth2.py @@ -4,7 +4,9 @@ from boxsdk.auth import redis_managed_oauth2 -def test_redis_managed_oauth2_gets_tokens_from_redis_on_init(access_token, refresh_token): +def test_redis_managed_oauth2_gets_tokens_from_redis_on_init( + access_token, refresh_token +): redis_server = Mock(redis_managed_oauth2.StrictRedis) redis_server.hvals.return_value = access_token, refresh_token unique_id = Mock() @@ -18,7 +20,9 @@ def test_redis_managed_oauth2_gets_tokens_from_redis_on_init(access_token, refre assert oauth2.unique_id is unique_id -def test_redis_managed_oauth2_gets_tokens_from_redis_during_refresh(access_token, refresh_token, new_access_token): +def test_redis_managed_oauth2_gets_tokens_from_redis_during_refresh( + access_token, refresh_token, new_access_token +): new_refresh_token = uuid.uuid4().hex redis_server = Mock(redis_managed_oauth2.StrictRedis) redis_server.hvals.return_value = new_access_token, new_refresh_token @@ -40,15 +44,17 @@ def test_redis_managed_oauth2_gets_tokens_from_redis_during_refresh(access_token def test_redis_managed_oauth2_stores_tokens_to_redis_during_refresh( - access_token, - refresh_token, - mock_box_session, - successful_token_response, + access_token, + refresh_token, + mock_box_session, + successful_token_response, ): redis_server = Mock(redis_managed_oauth2.StrictRedis) redis_server.hvals.return_value = access_token, refresh_token unique_id = Mock() - with patch.object(redis_managed_oauth2.RedisManagedOAuth2, '_update_current_tokens'): + with patch.object( + redis_managed_oauth2.RedisManagedOAuth2, '_update_current_tokens' + ): oauth2 = redis_managed_oauth2.RedisManagedOAuth2( client_id=None, client_secret=None, @@ -58,4 +64,6 @@ def test_redis_managed_oauth2_stores_tokens_to_redis_during_refresh( ) mock_box_session.request.return_value = successful_token_response oauth2.send_token_request({}, access_token=None, expect_refresh_token=True) - redis_server.hmset.assert_called_once_with(unique_id, {'access': access_token, 'refresh': refresh_token}) + redis_server.hmset.assert_called_once_with( + unique_id, {'access': access_token, 'refresh': refresh_token} + ) diff --git a/test/unit/client/test_client.py b/test/unit/client/test_client.py index 0c3b0e90a..e24648437 100644 --- a/test/unit/client/test_client.py +++ b/test/unit/client/test_client.py @@ -32,7 +32,11 @@ from boxsdk.object.upload_session import UploadSession from boxsdk.object.trash import Trash from boxsdk.object.group_membership import GroupMembership -from boxsdk.object.metadata_template import MetadataTemplate, MetadataField, MetadataFieldType +from boxsdk.object.metadata_template import ( + MetadataTemplate, + MetadataField, + MetadataFieldType, +) from boxsdk.object.retention_policy import RetentionPolicy from boxsdk.object.retention_policy_assignment import RetentionPolicyAssignment from boxsdk.object.file_version_retention import FileVersionRetention @@ -115,7 +119,9 @@ def mock_folder_response(mock_object_id, make_mock_box_request): @pytest.fixture(scope='function') def mock_content_response(make_mock_box_request): - mock_box_response, mock_network_response = make_mock_box_request(content=b'Contents of a text file.') + mock_box_response, mock_network_response = make_mock_box_request( + content=b'Contents of a text file.' + ) mock_network_response.response_as_stream = raw = Mock() raw.stream.return_value = (bytes((b,)) for b in mock_box_response.content) return mock_box_response @@ -133,11 +139,11 @@ def users_response(user_id_1, user_id_2): mock_network_response.json.return_value = { 'entries': [ {'type': 'user', 'id': user_id_1}, - {'type': 'user', 'id': user_id_2} + {'type': 'user', 'id': user_id_2}, ], 'limit': 100, 'offset': 0, - 'total_count': 2 + 'total_count': 2, } return mock_network_response @@ -197,7 +203,7 @@ def groups_response(group_id_1, group_id_2): ], 'limit': 100, 'offset': 0, - 'total_count': 2 + 'total_count': 2, } return mock_network_response @@ -218,8 +224,16 @@ def legal_hold_policies_response(legal_hold_policy_id_1, legal_hold_policy_id_2) mock_network_response = Mock(DefaultNetworkResponse) mock_network_response.json.return_value = { 'entries': [ - {'type': 'legal_hold_policy', 'id': legal_hold_policy_id_1, 'name': 'Test Policy 1'}, - {'type': 'legal_hold_policy', 'id': legal_hold_policy_id_2, 'name': 'Test Policy 2'}, + { + 'type': 'legal_hold_policy', + 'id': legal_hold_policy_id_1, + 'name': 'Test Policy 1', + }, + { + 'type': 'legal_hold_policy', + 'id': legal_hold_policy_id_2, + 'name': 'Test Policy 2', + }, ], 'limit': 5, } @@ -233,7 +247,7 @@ def create_policy_response(): mock_network_response.json.return_value = { 'type': 'legal_hold_policy', 'id': 1234, - 'policy_name': 'Test Policy' + 'policy_name': 'Test Policy', } return mock_network_response @@ -298,7 +312,9 @@ def create_invite_response(): @pytest.fixture(params=('file', 'folder')) -def test_item_and_response(mock_file, test_folder, mock_file_response, mock_folder_response, request): +def test_item_and_response( + mock_file, test_folder, mock_file_response, mock_folder_response, request +): if request.param == 'file': return mock_file, mock_file_response return test_folder, mock_folder_response @@ -345,11 +361,11 @@ def search_response(file_id, folder_id): mock_network_response.json.return_value = { 'entries': [ {'type': 'file', 'id': file_id}, - {'type': 'folder', 'id': folder_id} + {'type': 'folder', 'id': folder_id}, ], 'limit': 100, 'offset': 0, - 'total_count': 2 + 'total_count': 2, } return mock_network_response @@ -358,9 +374,7 @@ def search_response(file_id, folder_id): def recent_items_response(file_id): mock_network_response = Mock(DefaultNetworkResponse) mock_network_response.json.return_value = { - 'entries': [ - {'type': 'recent_item', 'item': {'type': 'file', 'id': file_id}} - ], + 'entries': [{'type': 'recent_item', 'item': {'type': 'file', 'id': file_id}}], 'next_marker': None, 'limit': 100, } @@ -391,25 +405,30 @@ def device_pins_response(device_pin_id_1, device_pin_id_2): return mock_network_response -@pytest.mark.parametrize('test_class, factory_method_name', [ - (EmailAlias, 'email_alias'), - (Enterprise, 'enterprise'), - (Folder, 'folder'), - (File, 'file'), - (FileVersion, 'file_version'), - (Invite, 'invite'), - (User, 'user'), - (Group, 'group'), - (GroupMembership, 'group_membership'), - (Enterprise, 'enterprise'), - (Webhook, 'webhook'), - (MetadataCascadePolicy, 'metadata_cascade_policy'), - (UploadSession, 'upload_session'), - (StoragePolicy, 'storage_policy'), - (StoragePolicyAssignment, 'storage_policy_assignment'), -]) -def test_factory_returns_the_correct_object(mock_client, test_class, factory_method_name): - """ Tests the various id-only factory methods in the Client class """ +@pytest.mark.parametrize( + 'test_class, factory_method_name', + [ + (EmailAlias, 'email_alias'), + (Enterprise, 'enterprise'), + (Folder, 'folder'), + (File, 'file'), + (FileVersion, 'file_version'), + (Invite, 'invite'), + (User, 'user'), + (Group, 'group'), + (GroupMembership, 'group_membership'), + (Enterprise, 'enterprise'), + (Webhook, 'webhook'), + (MetadataCascadePolicy, 'metadata_cascade_policy'), + (UploadSession, 'upload_session'), + (StoragePolicy, 'storage_policy'), + (StoragePolicyAssignment, 'storage_policy_assignment'), + ], +) +def test_factory_returns_the_correct_object( + mock_client, test_class, factory_method_name +): + """Tests the various id-only factory methods in the Client class""" # pylint:disable=redefined-outer-name fake_id = 'fake_id' @@ -448,19 +467,24 @@ def users_type(request): def test_users_return_the_correct_user_objects( - mock_client, - mock_box_session, - users_response, - user_id_1, - user_id_2, - users_filter_term, - users_type, - users_offset, - users_limit, + mock_client, + mock_box_session, + users_response, + user_id_1, + user_id_2, + users_filter_term, + users_type, + users_offset, + users_limit, ): # pylint:disable=redefined-outer-name mock_box_session.get.return_value = users_response - users = mock_client.users(limit=users_limit, offset=users_offset, filter_term=users_filter_term, user_type=users_type) + users = mock_client.users( + limit=users_limit, + offset=users_offset, + filter_term=users_filter_term, + user_type=users_type, + ) expected_params = {'offset': users_offset} if users_limit is not None: expected_params['limit'] = users_limit @@ -470,29 +494,37 @@ def test_users_return_the_correct_user_objects( expected_params['user_type'] = users_type assert users.next().object_id == user_id_1 assert users.next().object_id == user_id_2 - mock_box_session.get.assert_called_once_with(f'{API.BASE_API_URL}/users', params=expected_params) + mock_box_session.get.assert_called_once_with( + f'{API.BASE_API_URL}/users', params=expected_params + ) def test_users_return_the_correct_user_objects_marker( - mock_client, - mock_box_session, - user_id_1, - user_id_2, - users_filter_term, - users_type, - marker_id, - users_limit, + mock_client, + mock_box_session, + user_id_1, + user_id_2, + users_filter_term, + users_type, + marker_id, + users_limit, ): # pylint:disable=redefined-outer-name mock_box_session.get.return_value.json.return_value = { 'entries': [ {'type': 'user', 'id': user_id_1}, - {'type': 'user', 'id': user_id_2} + {'type': 'user', 'id': user_id_2}, ], 'next_marker': 'JGWUGKNJFSAH123NDSA', - 'total_count': 2 + 'total_count': 2, } - users = mock_client.users(limit=users_limit, marker=marker_id, use_marker=True, filter_term=users_filter_term, user_type=users_type) + users = mock_client.users( + limit=users_limit, + marker=marker_id, + use_marker=True, + filter_term=users_filter_term, + user_type=users_type, + ) expected_params = {'marker': marker_id, 'usemarker': True} if users_limit is not None: expected_params['limit'] = users_limit @@ -502,12 +534,14 @@ def test_users_return_the_correct_user_objects_marker( expected_params['user_type'] = users_type assert users.next().object_id == user_id_1 assert users.next().object_id == user_id_2 - mock_box_session.get.assert_called_once_with(f'{API.BASE_API_URL}/users', params=expected_params) + mock_box_session.get.assert_called_once_with( + f'{API.BASE_API_URL}/users', params=expected_params + ) def test_users_returns_correct_with_default_values( - mock_client, - mock_box_session, + mock_client, + mock_box_session, ): expected_url = f'{API.BASE_API_URL}/users' mock_box_session.get.return_value.json.return_value = { @@ -519,7 +553,7 @@ def test_users_returns_correct_with_default_values( 'type': 'user', 'id': '12345', } - ] + ], } users = mock_client.users() user = users.next() @@ -530,11 +564,11 @@ def test_users_returns_correct_with_default_values( def test_search_instantiates_search_and_calls_search( - mock_client, - mock_box_session, - search_response, - file_id, - folder_id, + mock_client, + mock_box_session, + search_response, + file_id, + folder_id, ): # pylint:disable=redefined-outer-name mock_box_session.get.return_value = search_response @@ -561,11 +595,11 @@ def test_collaboration_allowlist_initializer(mock_client): def test_get_groups_return_the_correct_group_objects( - mock_client, - mock_box_session, - groups_response, - group_id_1, - group_id_2, + mock_client, + mock_box_session, + groups_response, + group_id_1, + group_id_2, ): # pylint:disable=redefined-outer-name group_name = 'Employees' @@ -577,21 +611,27 @@ def test_get_groups_return_the_correct_group_objects( assert group.name == str(expected_id) # pylint:disable=protected-access assert group._session == mock_box_session - mock_box_session.get.assert_called_once_with(expected_url, params={'offset': None, 'filter_term': group_name}) + mock_box_session.get.assert_called_once_with( + expected_url, params={'offset': None, 'filter_term': group_name} + ) -def test_create_group_returns_the_correct_group_object(mock_client, mock_box_session, create_group_response): +def test_create_group_returns_the_correct_group_object( + mock_client, mock_box_session, create_group_response +): # pylint:disable=redefined-outer-name expected_url = f'{API.BASE_API_URL}/groups' test_group_name = 'test_group_name' - value = json.dumps({ - 'name': test_group_name, - 'provenance': 'Example', - 'external_sync_identifier': 'Example-User', - 'description': 'Description of group', - 'invitability_level': 'admins_and_members', - 'member_viewability_level': 'admins_only', - }) + value = json.dumps( + { + 'name': test_group_name, + 'provenance': 'Example', + 'external_sync_identifier': 'Example-User', + 'description': 'Description of group', + 'invitability_level': 'admins_and_members', + 'member_viewability_level': 'admins_only', + } + ) mock_box_session.post.return_value = create_group_response new_group = mock_client.create_group( name=test_group_name, @@ -603,31 +643,39 @@ def test_create_group_returns_the_correct_group_object(mock_client, mock_box_ses fields=['name,description'], ) - mock_box_session.post.assert_called_once_with(expected_url, data=value, params={'fields': 'name,description'}) + mock_box_session.post.assert_called_once_with( + expected_url, data=value, params={'fields': 'name,description'} + ) assert isinstance(new_group, Group) assert new_group.object_id == 1234 assert new_group.name == test_group_name @pytest.mark.parametrize('description', ('My test policy',)) -@pytest.mark.parametrize('filter_started_at', ('2016-01-01T00:00:00+00:00', datetime.datetime(2016, 1, 1, tzinfo=pytz.UTC))) -@pytest.mark.parametrize('filter_ended_at', ('2020-01-01T00:00:00+00:00', datetime.datetime(2020, 1, 1, tzinfo=pytz.UTC))) +@pytest.mark.parametrize( + 'filter_started_at', + ('2016-01-01T00:00:00+00:00', datetime.datetime(2016, 1, 1, tzinfo=pytz.UTC)), +) +@pytest.mark.parametrize( + 'filter_ended_at', + ('2020-01-01T00:00:00+00:00', datetime.datetime(2020, 1, 1, tzinfo=pytz.UTC)), +) @pytest.mark.parametrize('is_ongoing', ('True',)) def test_create_legal_hold_policy_returns_the_correct_policy_object( - mock_client, - mock_box_session, - create_policy_response, - description, - filter_started_at, - filter_ended_at, - is_ongoing + mock_client, + mock_box_session, + create_policy_response, + description, + filter_started_at, + filter_ended_at, + is_ongoing, ): # pylint:disable=redefined-outer-name params = { 'description': description, 'filter_started_at': filter_started_at, 'filter_ended_at': filter_ended_at, - 'is_ongoing': is_ongoing + 'is_ongoing': is_ongoing, } test_policy_name = 'Test Policy' @@ -642,7 +690,7 @@ def test_create_legal_hold_policy_returns_the_correct_policy_object( 'description': description, 'filter_started_at': normalize_date_to_rfc3339_format(filter_started_at), 'filter_ended_at': normalize_date_to_rfc3339_format(filter_ended_at), - 'is_ongoing': is_ongoing + 'is_ongoing': is_ongoing, } mock_box_session.post.assert_called_once_with(expected_url, data=ANY) assert dict(json.loads(mock_box_session.post.call_args[1]['data'])) == expected_body @@ -653,22 +701,26 @@ def test_create_legal_hold_policy_returns_the_correct_policy_object( def test_get_legal_hold_policies_return_the_correct_policy_objects( - mock_client, - mock_box_session, - legal_hold_policies_response, - legal_hold_policy_id_1, - legal_hold_policy_id_2, + mock_client, + mock_box_session, + legal_hold_policies_response, + legal_hold_policy_id_1, + legal_hold_policy_id_2, ): # pylint:disable=redefined-outer-name policy_name = 'Arbitration' expected_url = f'{API.BASE_API_URL}/legal_hold_policies' mock_box_session.get.return_value = legal_hold_policies_response policies = mock_client.get_legal_hold_policies(policy_name) - for policy, expected_id in zip(policies, [legal_hold_policy_id_1, legal_hold_policy_id_2]): + for policy, expected_id in zip( + policies, [legal_hold_policy_id_1, legal_hold_policy_id_2] + ): assert policy.object_id == expected_id # pylint:disable=protected-access assert policy._session == mock_box_session - mock_box_session.get.assert_called_once_with(expected_url, params={'policy_name': policy_name}) + mock_box_session.get.assert_called_once_with( + expected_url, params={'policy_name': policy_name} + ) def test_trash_initializer(mock_client): @@ -676,7 +728,9 @@ def test_trash_initializer(mock_client): assert isinstance(trash, Trash) -def test_get_recent_items_returns_the_correct_items(mock_client, mock_box_session, recent_items_response, file_id): +def test_get_recent_items_returns_the_correct_items( + mock_client, mock_box_session, recent_items_response, file_id +): mock_box_session.get.return_value = recent_items_response recent_items = mock_client.get_recent_items() assert isinstance(recent_items, MarkerBasedObjectCollection) @@ -686,7 +740,9 @@ def test_get_recent_items_returns_the_correct_items(mock_client, mock_box_sessio assert next_pointer is None -def test_get_recent_items_sends_get_with_correct_params(mock_client, mock_box_session, recent_items_response, marker_id): +def test_get_recent_items_sends_get_with_correct_params( + mock_client, mock_box_session, recent_items_response, marker_id +): limit = 50 marker = marker_id fields = ['modified_at', 'name'] @@ -696,13 +752,19 @@ def test_get_recent_items_sends_get_with_correct_params(mock_client, mock_box_se 'fields': ','.join(fields), } mock_box_session.get.return_value = recent_items_response - object_collection = mock_client.get_recent_items(limit=limit, marker=marker, fields=fields) + object_collection = mock_client.get_recent_items( + limit=limit, marker=marker, fields=fields + ) object_collection.next() - mock_box_session.get.assert_called_once_with(f'{API.BASE_API_URL}/recent_items', params=expected_params) + mock_box_session.get.assert_called_once_with( + f'{API.BASE_API_URL}/recent_items', params=expected_params + ) @pytest.mark.parametrize('password', (None, 'p4ssw0rd')) -def test_get_shared_item_returns_the_correct_item(mock_client, mock_box_session, shared_item_response, password): +def test_get_shared_item_returns_the_correct_item( + mock_client, mock_box_session, shared_item_response, password +): # pylint:disable=redefined-outer-name shared_link = 'https://cloud.box.com/s/661wcw2iz6q5r7v5xxkm' mock_box_session.request.return_value = shared_item_response @@ -712,25 +774,34 @@ def test_get_shared_item_returns_the_correct_item(mock_client, mock_box_session, 'GET', f'{API.BASE_API_URL}/shared_items', headers={ - 'BoxApi': f'shared_link={shared_link}{f"&shared_link_password={password}" if password is not None else ""}' + 'BoxApi': ( + f'shared_link={shared_link}{f"&shared_link_password={password}" if password is not None else ""}' + ) }, ) -@pytest.mark.parametrize('test_method', [ - 'get', - 'post', - 'put', - 'delete', - 'options', -]) -def test_make_request_passes_request_on_to_session(mock_client, mock_box_session, test_method): +@pytest.mark.parametrize( + 'test_method', + [ + 'get', + 'post', + 'put', + 'delete', + 'options', + ], +) +def test_make_request_passes_request_on_to_session( + mock_client, mock_box_session, test_method +): # pylint:disable=redefined-outer-name mock_client.make_request(test_method, 'url') assert mock_box_session.request.call_args[0] == (test_method, 'url') -def test_create_app_user_returns_the_correct_user_object(mock_client, mock_box_session, create_user_response): +def test_create_app_user_returns_the_correct_user_object( + mock_client, mock_box_session, create_user_response +): # pylint:disable=redefined-outer-name test_user_name = 'Ned Stark' value = json.dumps({'name': test_user_name, 'is_platform_access_only': True}) @@ -746,7 +817,9 @@ def test_create_app_user_returns_the_correct_user_object(mock_client, mock_box_s assert new_user.name == test_user_name -def test_create_enterprise_user_returns_the_correct_user_object(mock_client, mock_box_session, create_user_response): +def test_create_enterprise_user_returns_the_correct_user_object( + mock_client, mock_box_session, create_user_response +): # pylint:disable=redefined-outer-name test_user_name = 'Ned Stark' test_user_login = 'eddard@box.com' @@ -768,11 +841,11 @@ def test_get_storage_policies(mock_client, mock_box_session): mock_policy = { 'type': 'storage_policy', 'id': '12345', - 'name': 'Test Storage Policy' + 'name': 'Test Storage Policy', } mock_box_session.get.return_value.json.return_value = { 'limit': 100, - 'entries': [mock_policy] + 'entries': [mock_policy], } policies = mock_client.get_storage_policies() policy = policies.next() @@ -789,11 +862,13 @@ def test_create_terms_of_service(mock_client, mock_box_session): test_text = 'This is a test text' test_tos_type = 'external' test_status = 'enabled' - value = json.dumps({ - 'status': 'enabled', - 'tos_type': 'external', - 'text': 'This is a test text', - }) + value = json.dumps( + { + 'status': 'enabled', + 'tos_type': 'external', + 'text': 'This is a test text', + } + ) mock_box_session.post.return_value.json.return_value = { 'type': 'terms_of_service', 'id': '12345', @@ -801,7 +876,9 @@ def test_create_terms_of_service(mock_client, mock_box_session): 'tos_type': test_tos_type, 'text': test_text, } - new_terms_of_service = mock_client.create_terms_of_service('enabled', 'external', 'This is a test text') + new_terms_of_service = mock_client.create_terms_of_service( + 'enabled', 'external', 'This is a test text' + ) mock_box_session.post.assert_called_once_with(expected_url, data=value) assert isinstance(new_terms_of_service, TermsOfService) assert new_terms_of_service.type == 'terms_of_service' @@ -825,7 +902,9 @@ def test_get_all_terms_of_services(mock_client, mock_box_session): } services = mock_client.get_terms_of_services(tos_type='external') service = services.next() - mock_box_session.get.assert_called_once_with(expected_url, params={'tos_type': 'external'}) + mock_box_session.get.assert_called_once_with( + expected_url, params={'tos_type': 'external'} + ) assert isinstance(service, TermsOfService) assert service.type == 'terms_of_service' assert service.id == '12345' @@ -834,11 +913,11 @@ def test_get_all_terms_of_services(mock_client, mock_box_session): def test_create_webhook_returns_the_correct_policy_object( - test_item_and_response, - test_webhook, - mock_client, - mock_box_session, - create_webhook_response, + test_item_and_response, + test_webhook, + mock_client, + mock_box_session, + create_webhook_response, ): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response @@ -853,7 +932,9 @@ def test_create_webhook_returns_the_correct_policy_object( } value = json.dumps(expected_body) mock_box_session.post.return_value = create_webhook_response - new_webhook = mock_client.create_webhook(test_item, ['FILE.UPLOADED', 'FOLDER.CREATED'], 'https://test.com') + new_webhook = mock_client.create_webhook( + test_item, ['FILE.UPLOADED', 'FOLDER.CREATED'], 'https://test.com' + ) mock_box_session.post.assert_called_once_with( expected_url, data=value, @@ -950,12 +1031,16 @@ def test_create_retention_policy(mock_client, mock_box_session, mock_user_list): retention_type='modifiable', description=policy_description, ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert policy.object_id == mock_policy['id'] assert policy.object_type == mock_policy['type'] assert policy.policy_name == mock_policy['policy_name'] assert policy.disposition_action == mock_policy['disposition_action'] - assert policy.can_owner_extend_retention == mock_policy['can_owner_extend_retention'] + assert ( + policy.can_owner_extend_retention == mock_policy['can_owner_extend_retention'] + ) assert policy.are_owners_notified == mock_policy['are_owners_notified'] assert policy.retention_type == mock_policy['retention_type'] assert policy.description == mock_policy['description'] @@ -998,12 +1083,16 @@ def test_create_infinte_retention_policy(mock_client, mock_box_session): retention_type='non_modifiable', description=policy_description, ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert policy.object_id == mock_policy['id'] assert policy.object_type == mock_policy['type'] assert policy.policy_name == mock_policy['policy_name'] assert policy.disposition_action == mock_policy['disposition_action'] - assert policy.can_owner_extend_retention == mock_policy['can_owner_extend_retention'] + assert ( + policy.can_owner_extend_retention == mock_policy['can_owner_extend_retention'] + ) assert policy.are_owners_notified == mock_policy['are_owners_notified'] assert policy.retention_type == mock_policy['retention_type'] assert policy.description == mock_policy['description'] @@ -1022,7 +1111,9 @@ def test_get_retention_policies(mock_client, mock_box_session, mock_user, mock_u 'entries': [mock_policy], 'next_marker': 'testMarker', } - policies = mock_client.get_retention_policies(policy_name='Test Name', policy_type='finite', user=mock_user) + policies = mock_client.get_retention_policies( + policy_name='Test Name', policy_type='finite', user=mock_user + ) policy = policies.next() params = { 'policy_name': 'Test Name', @@ -1035,7 +1126,9 @@ def test_get_retention_policies(mock_client, mock_box_session, mock_user, mock_u assert policy.name == mock_policy['name'] -def test_get_file_version_retentions(mock_client, mock_box_session, mock_file, mock_retention_policy): +def test_get_file_version_retentions( + mock_client, mock_box_session, mock_file, mock_retention_policy +): expected_url = f'{API.BASE_API_URL}/file_version_retentions' mock_retention = { 'type': 'file_version_retention', @@ -1086,7 +1179,9 @@ def test_get_pending_collaborations(mock_client, mock_box_session): } pending_collaborations = mock_client.get_pending_collaborations(limit=2) pending_collaboration = pending_collaborations.next() - mock_box_session.get.assert_called_once_with(expected_url, params={'limit': 2, 'status': 'pending', 'offset': None}) + mock_box_session.get.assert_called_once_with( + expected_url, params={'limit': 2, 'status': 'pending', 'offset': None} + ) assert isinstance(pending_collaboration, Collaboration) assert pending_collaboration.id == mock_collaboration['id'] assert pending_collaboration.type == mock_collaboration['type'] @@ -1096,29 +1191,36 @@ def test_get_pending_collaborations(mock_client, mock_box_session): @pytest.fixture def check_downscope_token_request( - mock_client, - mock_box_session, - mock_object_id, - make_mock_box_request, + mock_client, + mock_box_session, + mock_object_id, + make_mock_box_request, ): def do_check(scopes, item_class, additional_data, shared_link, expected_data): temp_downscoped_token = 'temp_downscoped_token' temp_expires_in = 1234 mock_box_response, _ = make_mock_box_request( - response={'access_token': temp_downscoped_token, 'expires_in': temp_expires_in}, + response={ + 'access_token': temp_downscoped_token, + 'expires_in': temp_expires_in, + }, ) mock_box_session.post.return_value = mock_box_response item = item_class(mock_box_session, mock_object_id) if item_class else None - downscoped_token_response = mock_client.downscope_token(scopes, item, additional_data, shared_link) + downscoped_token_response = mock_client.downscope_token( + scopes, item, additional_data, shared_link + ) assert downscoped_token_response.access_token == temp_downscoped_token assert downscoped_token_response.expires_in == temp_expires_in if item: expected_data['resource'] = item.get_url() - mock_box_session.post.assert_called_once_with(f'{API.OAUTH2_API_URL}/token', data=expected_data) + mock_box_session.post.assert_called_once_with( + f'{API.OAUTH2_API_URL}/token', data=expected_data + ) return do_check @@ -1127,17 +1229,25 @@ def do_check(scopes, item_class, additional_data, shared_link, expected_data): 'item_class,scopes,expected_scopes', [ (File, [TokenScope.ITEM_READWRITE], 'item_readwrite'), - (Folder, [TokenScope.ITEM_PREVIEW, TokenScope.ITEM_SHARE], 'item_preview item_share'), - (File, [TokenScope.ITEM_READ, TokenScope.ITEM_SHARE, TokenScope.ITEM_DELETE], 'item_read item_share item_delete'), + ( + Folder, + [TokenScope.ITEM_PREVIEW, TokenScope.ITEM_SHARE], + 'item_preview item_share', + ), + ( + File, + [TokenScope.ITEM_READ, TokenScope.ITEM_SHARE, TokenScope.ITEM_DELETE], + 'item_read item_share item_delete', + ), (None, [TokenScope.ITEM_DOWNLOAD], 'item_download'), ], ) def test_downscope_token_sends_downscope_request( - mock_client, - check_downscope_token_request, - item_class, - scopes, - expected_scopes, + mock_client, + check_downscope_token_request, + item_class, + scopes, + expected_scopes, ): mock_client.auth.access_token = 'existing_access_token' expected_data = { @@ -1150,8 +1260,8 @@ def test_downscope_token_sends_downscope_request( def test_downscope_token_sends_downscope_request_with_shared_link( - mock_client, - check_downscope_token_request, + mock_client, + check_downscope_token_request, ): shared_link = 'https://cloud.box.com/s/foo' expected_data = { @@ -1159,16 +1269,21 @@ def test_downscope_token_sends_downscope_request_with_shared_link( 'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token', 'scope': 'item_readwrite', 'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange', - 'box_shared_link': shared_link + 'box_shared_link': shared_link, } - check_downscope_token_request([TokenScope.ITEM_READWRITE], None, None, shared_link, expected_data) + check_downscope_token_request( + [TokenScope.ITEM_READWRITE], None, None, shared_link, expected_data + ) def test_downscope_token_sends_downscope_request_with_additional_data( - mock_client, - check_downscope_token_request, + mock_client, + check_downscope_token_request, ): - additional_data = {'grant_type': 'new_grant_type', 'extra_data_key': 'extra_data_value'} + additional_data = { + 'grant_type': 'new_grant_type', + 'extra_data_key': 'extra_data_value', + } expected_data = { 'subject_token': mock_client.auth.access_token, 'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token', @@ -1176,12 +1291,14 @@ def test_downscope_token_sends_downscope_request_with_additional_data( 'grant_type': 'new_grant_type', 'extra_data_key': 'extra_data_value', } - check_downscope_token_request([TokenScope.ITEM_READWRITE], File, additional_data, None, expected_data) + check_downscope_token_request( + [TokenScope.ITEM_READWRITE], File, additional_data, None, expected_data + ) def test_downscope_token_sends_downscope_request_when_no_initial_token( - mock_client, - check_downscope_token_request, + mock_client, + check_downscope_token_request, ): mock_client.auth.access_token = None mock_client.auth.refresh.return_value = 'new_access_token' @@ -1192,11 +1309,19 @@ def test_downscope_token_sends_downscope_request_when_no_initial_token( 'scope': 'item_readwrite', 'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange', } - check_downscope_token_request([TokenScope.ITEM_READWRITE], File, None, None, expected_data) + check_downscope_token_request( + [TokenScope.ITEM_READWRITE], File, None, None, expected_data + ) mock_client.auth.refresh.assert_called_once_with(None) -def test_device_pins_for_enterprise(mock_client, mock_box_session, device_pins_response, device_pin_id_1, device_pin_id_2): +def test_device_pins_for_enterprise( + mock_client, + mock_box_session, + device_pins_response, + device_pin_id_1, + device_pin_id_2, +): # pylint:disable=redefined-outer-name enterprise_id = '11111' expected_url = f'{API.BASE_API_URL}/enterprises/{enterprise_id}/device_pinners' @@ -1207,7 +1332,9 @@ def test_device_pins_for_enterprise(mock_client, mock_box_session, device_pins_r assert pin.object_id == expected_id # pylint:disable=protected-access assert pin._session == mock_box_session - mock_box_session.get.assert_called_once_with(expected_url, params={'direction': 'ASC'}) + mock_box_session.get.assert_called_once_with( + expected_url, params={'direction': 'ASC'} + ) def test_metadata_template_initializer(mock_client, mock_box_session): @@ -1307,9 +1434,13 @@ def test_create_metadata_template(mock_client, mock_box_session): response.update(expected_body) mock_box_session.post.return_value.json.return_value = response - template = mock_client.create_metadata_template(name, [field1, field2], key, hidden=True) + template = mock_client.create_metadata_template( + name, [field1, field2], key, hidden=True + ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(template, MetadataTemplate) assert template.object_id is None assert template.displayName == 'Vendor Contract' @@ -1323,9 +1454,7 @@ def test_create_metadata_template(mock_client, mock_box_session): def test_get_current_enterprise(mock_client, mock_box_session): expected_url = f'{API.BASE_API_URL}/users/me' - expected_params = { - 'fields': 'enterprise' - } + expected_params = {'fields': 'enterprise'} enterprise_id = '44444' enterprise_name = 'Acme, Inc.' user_json = { @@ -1341,12 +1470,16 @@ def test_get_current_enterprise(mock_client, mock_box_session): enterprise = mock_client.get_current_enterprise() - mock_box_session.get.assert_called_once_with(expected_url, params=expected_params, headers=None) + mock_box_session.get.assert_called_once_with( + expected_url, params=expected_params, headers=None + ) assert isinstance(enterprise, Enterprise) assert enterprise.object_id == enterprise_id assert enterprise._session == mock_box_session # pylint:disable=protected-access assert enterprise.name == enterprise_name - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params={'fields': 'enterprise'}) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params={'fields': 'enterprise'} + ) def test_comment(mock_client): @@ -1422,7 +1555,9 @@ def test_collections(mock_client, mock_box_session): } collections = mock_client.collections(limit=2) collection = collections.next() - mock_box_session.get.assert_called_once_with(expected_url, params={'limit': 2, 'offset': 0}) + mock_box_session.get.assert_called_once_with( + expected_url, params={'limit': 2, 'offset': 0} + ) assert isinstance(collection, Collection) assert collection.id == mock_collection['id'] assert collection.type == mock_collection['type'] @@ -1503,15 +1638,9 @@ def test_download_zip(mock_client, mock_box_session, mock_content_response): expected_create_body = { 'download_file_name': name, 'items': [ - { - 'type': 'file', - 'id': '466239504569' - }, - { - 'type': 'folder', - 'id': '466239504580' - } - ] + {'type': 'file', 'id': '466239504569'}, + {'type': 'folder', 'id': '466239504580'}, + ], } status_response_mock = Mock() status_response_mock.json.return_value = { @@ -1519,10 +1648,12 @@ def test_download_zip(mock_client, mock_box_session, mock_content_response): 'downloaded_file_count': 10, 'skipped_file_count': 10, 'skipped_folder_count': 10, - 'state': 'succeeded' + 'state': 'succeeded', } mock_box_session.post.return_value.json.return_value = { - 'download_url': 'https://dl.boxcloud.com/2.0/zip_downloads/124hfiowk3fa8kmrwh/content', + 'download_url': ( + 'https://dl.boxcloud.com/2.0/zip_downloads/124hfiowk3fa8kmrwh/content' + ), 'status_url': 'https://api.box.com/2.0/zip_downloads/124hfiowk3fa8kmrwh/status', 'expires_at': '2018-04-25T11:00:18-07:00', 'name_conflicts': [ @@ -1531,27 +1662,34 @@ def test_download_zip(mock_client, mock_box_session, mock_content_response): 'id': '100', 'type': 'file', 'original_name': 'salary.pdf', - 'download_name': 'aqc823.pdf' + 'download_name': 'aqc823.pdf', }, { 'id': '200', 'type': 'file', 'original_name': 'salary.pdf', - 'download_name': 'aci23s.pdf' - } + 'download_name': 'aci23s.pdf', + }, ] - ] + ], } mock_box_session.get.side_effect = [mock_content_response, status_response_mock] status_returned = mock_client.download_zip(name, items, mock_writeable_stream) - mock_box_session.post.assert_called_once_with(expected_create_url, - data=json.dumps(expected_create_body), - skip_retry_codes={202}) - mock_box_session.get.assert_any_call('https://dl.boxcloud.com/2.0/zip_downloads/124hfiowk3fa8kmrwh/content', - expect_json_response=False, stream=True) - mock_box_session.get.assert_called_with('https://api.box.com/2.0/zip_downloads/124hfiowk3fa8kmrwh/status') + mock_box_session.post.assert_called_once_with( + expected_create_url, + data=json.dumps(expected_create_body), + skip_retry_codes={202}, + ) + mock_box_session.get.assert_any_call( + 'https://dl.boxcloud.com/2.0/zip_downloads/124hfiowk3fa8kmrwh/content', + expect_json_response=False, + stream=True, + ) + mock_box_session.get.assert_called_with( + 'https://api.box.com/2.0/zip_downloads/124hfiowk3fa8kmrwh/status' + ) mock_writeable_stream.seek(0) assert mock_writeable_stream.read() == mock_content_response.content assert status_returned['total_file_count'] == 20 @@ -1579,14 +1717,14 @@ def mock_sign_request_response(): 'type': 'folder', 'etag': '1', 'name': 'Contracts', - 'sequence_id': '3' + 'sequence_id': '3', }, 'prefill_tags': [ { 'document_tag_id': '1234', 'text_value': 'text', 'checkbox_value': 'true', - 'date_value': '2021-04-26T08:12:13.982Z' + 'date_value': '2021-04-26T08:12:13.982Z', } ], 'prepare_url': 'https://prepareurl.com', @@ -1602,11 +1740,11 @@ def mock_sign_request_response(): 'file_version': { 'id': '12345', 'type': 'file_version', - 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc' - } + 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc', + }, } ], - 'is_ready_for_download': 'true' + 'is_ready_for_download': 'true', }, 'signers': [ { @@ -1618,7 +1756,7 @@ def mock_sign_request_response(): 'has_viewed_document': 'true', 'signer_decision': { 'type': 'signed', - 'finalized_at': '2021-04-26T08:12:13.982Z' + 'finalized_at': '2021-04-26T08:12:13.982Z', }, 'inputs': [ { @@ -1627,10 +1765,10 @@ def mock_sign_request_response(): 'checkbox_value': 'true', 'date_value': '2021-04-26T08:12:13.982Z', 'type': 'text', - 'page_index': '4' + 'page_index': '4', } ], - 'embed_url': 'https://example.com' + 'embed_url': 'https://example.com', } ], 'signing_log': { @@ -1640,11 +1778,11 @@ def mock_sign_request_response(): 'file_version': { 'id': '12345', 'type': 'file_version', - 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc' + 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc', }, 'name': 'Contract.pdf', 'sequence_id': '3', - 'sha1': '85136C79CBF9FE36BB9D05D0639C70C265C18D37' + 'sha1': '85136C79CBF9FE36BB9D05D0639C70C265C18D37', }, 'source_files': [ { @@ -1657,12 +1795,12 @@ def mock_sign_request_response(): 'file_version': { 'id': '12345', 'type': 'file_version', - 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc' - } + 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc', + }, } ], 'status': 'converting', - 'template_id': '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb' + 'template_id': '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb', } return mock_sign_request @@ -1674,13 +1812,15 @@ def mock_sign_template_response(): "type": "sign-template", "name": "important-file.pdf", "email_message": "Please sign this document.\n\nKind regards", - "email_subject": "Box User (boxuser@box.com) has requested your signature on a document", + "email_subject": ( + "Box User (boxuser@box.com) has requested your signature on a document" + ), "parent_folder": { "id": "123456789", "etag": "0", "type": "folder", "sequence_id": "0", - "name": "My Sign Requests" + "name": "My Sign Requests", }, "auto_expire_days": "null", "source_files": [ @@ -1693,8 +1833,8 @@ def mock_sign_template_response(): "file_version": { "id": "123456", "type": "file_version", - "sha1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - } + "sha1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + }, } ], "are_email_settings_locked": "false", @@ -1710,7 +1850,7 @@ def mock_sign_template_response(): "role": "final_copy_reader", "is_in_person": "false", "order": 1, - "inputs": [] + "inputs": [], }, { "email": "", @@ -1728,11 +1868,11 @@ def mock_sign_template_response(): "is_required": True, "coordinates": { "x": 0.27038464059712, - "y": 0.10051756244533624 + "y": 0.10051756244533624, }, "dimensions": { "width": 0.23570031566618235, - "height": 0.04781003891921971 + "height": 0.04781003891921971, }, "date_value": None, "page_index": 0, @@ -1741,27 +1881,18 @@ def mock_sign_template_response(): "content_type": "signature", "dropdown_choices": None, "group_id": None, - "label": None + "label": None, } - ] - } + ], + }, ], "ready_sign_link": None, "custom_branding": None, "days_valid": 0, "additional_info": { "non_editable": [], - "required": { - "signers": [ - [ - "email" - ], - [ - "email" - ] - ] - } - } + "required": {"signers": [["email"], ["email"]]}, + }, } return mock_sign_template @@ -1784,10 +1915,7 @@ def mock_ai_agent_default_config_response(): 'content_template': '---{content}---', 'embeddings': { 'model': 'openai__text_embedding_ada_002', - 'strategy': { - 'id': 'basic', - 'num_tokens_per_chunk': 64 - } + 'strategy': {'id': 'basic', 'num_tokens_per_chunk': 64}, }, 'llm_endpoint_params': { 'type': 'openai_params', @@ -1795,14 +1923,18 @@ def mock_ai_agent_default_config_response(): 'presence_penalty': 1.5, 'stop': '<|im_end|>', 'temperature': 0, - 'top_p': 1 + 'top_p': 1, }, 'model': 'openai__gpt_3_5_turbo', 'num_tokens_for_completion': 8400, - 'prompt_template': 'It is `{current_date}`, and I have $8000 and want to spend a week in the Azores. What ' - 'should I see?', - 'system_message': 'You are a helpful travel assistant specialized in budget travel' - } + 'prompt_template': ( + 'It is `{current_date}`, and I have $8000 and want to spend a week in the Azores. What ' + 'should I see?' + ), + 'system_message': ( + 'You are a helpful travel assistant specialized in budget travel' + ), + }, } return mock_ai_agent_default_config_response @@ -1833,52 +1965,37 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re redirect_url = 'https://www.box.com/accepted' declined_redirect_url = 'https://www.box.com/declined' template_id = '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb' - source_file = { - 'id': '12345', - 'type': 'file' - } - source_file2 = { - 'id': '34567', - 'type': 'file' - } + source_file = {'id': '12345', 'type': 'file'} + source_file2 = {'id': '34567', 'type': 'file'} files = [source_file, source_file2] - signer = { - 'email': 'example@gmail.com' - } + signer = {'email': 'example@gmail.com'} signers = [signer] parent_folder_id = '12345' - data = json.dumps({ - 'signers': [ - { - 'email': signer['email'] - } - ], - 'source_files': [ - { - 'id': source_file['id'], - 'type': source_file['type'] - }, - { - 'id': source_file2['id'], - 'type': source_file2['type'] - } - ], - 'parent_folder': + data = json.dumps( { - 'id': parent_folder_id, - 'type': 'folder' - }, - 'redirect_url': redirect_url, - 'declined_redirect_url': declined_redirect_url, - 'template_id': template_id - }) + 'signers': [{'email': signer['email']}], + 'source_files': [ + {'id': source_file['id'], 'type': source_file['type']}, + {'id': source_file2['id'], 'type': source_file2['type']}, + ], + 'parent_folder': {'id': parent_folder_id, 'type': 'folder'}, + 'redirect_url': redirect_url, + 'declined_redirect_url': declined_redirect_url, + 'template_id': template_id, + } + ) mock_box_session.post.return_value.json.return_value = mock_sign_request_response new_sign_request = mock_client.create_sign_request( - files, signers, parent_folder_id, - redirect_url=redirect_url, declined_redirect_url=declined_redirect_url, template_id=template_id) + files, + signers, + parent_folder_id, + redirect_url=redirect_url, + declined_redirect_url=declined_redirect_url, + template_id=template_id, + ) mock_box_session.post.assert_called_once_with(expected_url, data=data) assert isinstance(new_sign_request, SignRequest) @@ -1890,32 +2007,33 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re assert new_sign_request['template_id'] == template_id -def test_create_sign_request_v2(mock_client, mock_box_session, mock_sign_request_response): +def test_create_sign_request_v2( + mock_client, mock_box_session, mock_sign_request_response +): expected_url = f'{API.BASE_API_URL}/sign_requests' redirect_url = 'https://www.box.com/accepted' declined_redirect_url = 'https://www.box.com/declined' template_id = '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb' - signer = { - 'email': 'example@gmail.com' - } + signer = {'email': 'example@gmail.com'} signers = [signer] - data = json.dumps({ - 'signers': [ - { - 'email': signer['email'] - } - ], - 'redirect_url': redirect_url, - 'declined_redirect_url': declined_redirect_url, - 'template_id': template_id - }) + data = json.dumps( + { + 'signers': [{'email': signer['email']}], + 'redirect_url': redirect_url, + 'declined_redirect_url': declined_redirect_url, + 'template_id': template_id, + } + ) mock_box_session.post.return_value.json.return_value = mock_sign_request_response new_sign_request = mock_client.create_sign_request_v2( signers, - redirect_url=redirect_url, declined_redirect_url=declined_redirect_url, template_id=template_id) + redirect_url=redirect_url, + declined_redirect_url=declined_redirect_url, + template_id=template_id, + ) mock_box_session.post.assert_called_once_with(expected_url, data=data) assert isinstance(new_sign_request, SignRequest) @@ -1934,14 +2052,18 @@ def test_file_request(mock_client): assert file_request.object_id == file_request_id -def test_get_sign_templates_from_id(mock_client, mock_box_session, mock_sign_template_response): +def test_get_sign_templates_from_id( + mock_client, mock_box_session, mock_sign_template_response +): test_sign_template_id = '93153068-5420-467b-b8ef-8e54bfb7be42' expected_url = f'{API.BASE_API_URL}/sign_templates/{test_sign_template_id}' mock_box_session.get.return_value.json.return_value = mock_sign_template_response sign_template = mock_client.sign_template(test_sign_template_id).get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(sign_template, SignTemplate) assert sign_template.id == '93153068-5420-467b-b8ef-8e54bfb7be42' @@ -1988,33 +2110,34 @@ def test_send_ai_question(mock_client, mock_box_session, mock_ai_question_respon expected_url = f'{API.BASE_API_URL}/ai/ask' mock_box_session.post.return_value.json.return_value = mock_ai_question_response - items = [{ - 'type': 'file', - 'id': '12345' - }] + items = [{'type': 'file', 'id': '12345'}] question = 'Why are public APIs important?' mode = 'single_item_qa' ai_agent = { 'type': 'ai_agent_ask', - 'basic_text_multi': { - 'model': 'openai__gpt_3_5_turbo' - } + 'basic_text_multi': {'model': 'openai__gpt_3_5_turbo'}, } answer = mock_client.send_ai_question(items, question, mode, ai_agent) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps({ - 'items': items, - 'prompt': question, - 'mode': mode, - 'ai_agent': { - 'type': 'ai_agent_ask', - 'basic_text_multi': { - 'model': 'openai__gpt_3_5_turbo' + mock_box_session.post.assert_called_once_with( + expected_url, + data=json.dumps( + { + 'items': items, + 'prompt': question, + 'mode': mode, + 'ai_agent': { + 'type': 'ai_agent_ask', + 'basic_text_multi': {'model': 'openai__gpt_3_5_turbo'}, + }, } - } - })) - assert answer['answer'] == 'Public APIs are important because of key and important reasons.' + ), + ) + assert ( + answer['answer'] + == 'Public APIs are important because of key and important reasons.' + ) assert answer['completion_reason'] == 'done' assert answer['created_at'] == '2021-04-26T08:12:13.982Z' @@ -2023,54 +2146,73 @@ def test_send_ai_text_gen(mock_client, mock_box_session, mock_ai_question_respon expected_url = f'{API.BASE_API_URL}/ai/text_gen' mock_box_session.post.return_value.json.return_value = mock_ai_question_response - items = [{ - 'type': 'file', - 'id': '12345' - }] - dialogue_history = [{ - "prompt": "Make my email about public APIs sound more professional", - "answer": "Here is the first draft of your professional email about public APIs", - "created_at": "2013-12-12T10:53:43-08:00" - }, { - "prompt": "Can you add some more information?", - "answer": "Public API schemas provide necessary information to integrate with APIs...", - "created_at": "2013-12-12T11:20:43-08:00" - }] + items = [{'type': 'file', 'id': '12345'}] + dialogue_history = [ + { + "prompt": "Make my email about public APIs sound more professional", + "answer": ( + "Here is the first draft of your professional email about public APIs" + ), + "created_at": "2013-12-12T10:53:43-08:00", + }, + { + "prompt": "Can you add some more information?", + "answer": ( + "Public API schemas provide necessary information to integrate with APIs..." + ), + "created_at": "2013-12-12T11:20:43-08:00", + }, + ] ai_agent = { 'type': 'ai_agent_text_gen', - 'basic_gen': { - 'model': 'openai__gpt_3_5_turbo_16k' - } + 'basic_gen': {'model': 'openai__gpt_3_5_turbo_16k'}, } answer = mock_client.send_ai_text_gen( dialogue_history=dialogue_history, items=items, prompt="Write an email to a client about the importance of public APIs.", - ai_agent=ai_agent + ai_agent=ai_agent, ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps({ - 'dialogue_history': dialogue_history, - 'items': items, - 'prompt': "Write an email to a client about the importance of public APIs.", - 'ai_agent': ai_agent - })) - assert answer['answer'] == 'Public APIs are important because of key and important reasons.' + mock_box_session.post.assert_called_once_with( + expected_url, + data=json.dumps( + { + 'dialogue_history': dialogue_history, + 'items': items, + 'prompt': ( + "Write an email to a client about the importance of public APIs." + ), + 'ai_agent': ai_agent, + } + ), + ) + assert ( + answer['answer'] + == 'Public APIs are important because of key and important reasons.' + ) assert answer['completion_reason'] == 'done' assert answer['created_at'] == '2021-04-26T08:12:13.982Z' -def test_get_ai_agent_default_config(mock_client, mock_box_session, mock_ai_agent_default_config_response): +def test_get_ai_agent_default_config( + mock_client, mock_box_session, mock_ai_agent_default_config_response +): expected_url = f'{API.BASE_API_URL}/ai_agent_default' - mock_box_session.get.return_value.json.return_value = mock_ai_agent_default_config_response + mock_box_session.get.return_value.json.return_value = ( + mock_ai_agent_default_config_response + ) config = mock_client.get_ai_agent_default_config( - mode='text_gen', - language='en', - model='openai__gpt_3_5_turbo' + mode='text_gen', language='en', model='openai__gpt_3_5_turbo' ) - mock_box_session.get.assert_called_once_with(expected_url, params={'mode': 'text_gen', 'language': 'en', 'model': 'openai__gpt_3_5_turbo'}) + mock_box_session.get.assert_called_once_with( + expected_url, + params={'mode': 'text_gen', 'language': 'en', 'model': 'openai__gpt_3_5_turbo'}, + ) assert config['type'] == 'ai_agent_text_gen' assert config['basic_gen']['model'] == 'openai__gpt_3_5_turbo' - assert config['basic_gen']['embeddings']['model'] == 'openai__text_embedding_ada_002' + assert ( + config['basic_gen']['embeddings']['model'] == 'openai__text_embedding_ada_002' + ) diff --git a/test/unit/conftest.py b/test/unit/conftest.py index 454340f90..d7622bbd8 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -15,7 +15,7 @@ @pytest.fixture(scope='function', autouse=True) def original_default_translator(): """A reference to the default translator, before the reference is changed by `default_translator` below.""" - return Translator._default_translator # pylint:disable=protected-access + return Translator._default_translator # pylint:disable=protected-access @pytest.yield_fixture(scope='function', autouse=True) @@ -28,15 +28,21 @@ def default_translator(original_default_translator): we reset the reference. """ try: - translator = Translator(dict(copy.deepcopy(original_default_translator)), extend_default_translator=False, new_child=False) - Translator._default_translator = translator # pylint:disable=protected-access + translator = Translator( + dict(copy.deepcopy(original_default_translator)), + extend_default_translator=False, + new_child=False, + ) + Translator._default_translator = translator # pylint:disable=protected-access yield translator finally: - Translator._default_translator = original_default_translator # pylint:disable=protected-access + Translator._default_translator = ( + original_default_translator # pylint:disable=protected-access + ) @pytest.fixture(scope='function') -def translator(default_translator): # pylint:disable=unused-argument +def translator(default_translator): # pylint:disable=unused-argument return Translator(extend_default_translator=True, new_child=True) @@ -48,7 +54,9 @@ def mock_box_session(translator): mock_session._client_config = mock_session.client_config = Client() mock_session._proxy_config = mock_session.proxy_config = Proxy() # pylint:enable=protected-access - mock_session.get_url.side_effect = lambda *args, **kwargs: Session.get_url(mock_session, *args, **kwargs) + mock_session.get_url.side_effect = lambda *args, **kwargs: Session.get_url( + mock_session, *args, **kwargs + ) mock_session.translator = translator return mock_session @@ -61,7 +69,9 @@ def mock_box_session_2(translator): mock_session._client_config = Client() mock_session._proxy_config = Proxy() # pylint:enable=protected-access - mock_session.get_url.side_effect = lambda *args, **kwargs: Session.get_url(mock_session, *args, **kwargs) + mock_session.get_url.side_effect = lambda *args, **kwargs: Session.get_url( + mock_session, *args, **kwargs + ) mock_session.translator = translator return mock_session @@ -109,6 +119,7 @@ def inner(status_code=200, response_ok=True, response=None, content=None): else: mock_box_response.content = content return mock_box_response, mock_network_response + return inner diff --git a/test/unit/network/conftest.py b/test/unit/network/conftest.py index 1f7252dc8..5a1c3771b 100644 --- a/test/unit/network/conftest.py +++ b/test/unit/network/conftest.py @@ -16,8 +16,19 @@ def mock_request(monkeypatch): return session.request -@pytest.fixture(params=['generic_successful_request_response', 'server_error_request_response_502', 'server_error_request_response_503']) -def request_response(generic_successful_request_response, server_error_request_response_502, server_error_request_response_503, request): +@pytest.fixture( + params=[ + 'generic_successful_request_response', + 'server_error_request_response_502', + 'server_error_request_response_503', + ] +) +def request_response( + generic_successful_request_response, + server_error_request_response_502, + server_error_request_response_503, + request, +): # pylint:disable=unused-argument return locals()[request.param] diff --git a/test/unit/network/test_network.py b/test/unit/network/test_network.py index 9a820a666..6fa4b55d5 100644 --- a/test/unit/network/test_network.py +++ b/test/unit/network/test_network.py @@ -35,7 +35,17 @@ def mocked_logger(logger): @pytest.fixture(scope='module') def logger_method_names(): - return ['critical', 'debug', 'error', 'exception', 'fatal', 'info', 'log', 'warn', 'warning'] + return [ + 'critical', + 'debug', + 'error', + 'exception', + 'fatal', + 'info', + 'log', + 'warn', + 'warning', + ] @pytest.fixture @@ -94,7 +104,7 @@ def _construct_network_response(request_response, log_response_content=True): return DefaultNetworkResponse( request_response=request_response, access_token_used=access_token, - log_response_content=log_response_content + log_response_content=log_response_content, ) return _construct_network_response @@ -133,7 +143,9 @@ def _do_not_get_content_from_response(response): # BEGIN Tests for DefaultNetwork. -def test_default_network_response_properties_pass_through_to_session_response_properties(access_token): +def test_default_network_response_properties_pass_through_to_session_response_properties( + access_token, +): mock_session_response = Mock(Response) mock_session_response.status_code = 200 mock_session_response.headers = {} @@ -183,7 +195,9 @@ def network_response_constructor(self): assert isinstance(response, DefaultNetworkResponseSubclass) -def test_network_logs_requests(make_network_request, http_verb, test_url, network, logger): +def test_network_logs_requests( + make_network_request, http_verb, test_url, network, logger +): kwargs = dict(custom_kwarg='foo') make_network_request(network, **kwargs) logger.info.assert_any_call( @@ -192,18 +206,29 @@ def test_network_logs_requests(make_network_request, http_verb, test_url, networ ) -def test_network_logs_request_exception(make_network_request, mock_request, http_verb, test_url, logger, network): - mock_request.side_effect = expected_exception = ExceptionSubclass('exception raised from request()') +def test_network_logs_request_exception( + make_network_request, mock_request, http_verb, test_url, logger, network +): + mock_request.side_effect = expected_exception = ExceptionSubclass( + 'exception raised from request()' + ) with pytest.raises(ExceptionSubclass) as pytest_exc_info: make_network_request(network) assert pytest_exc_info.value is expected_exception logger.warning.assert_called_once_with( network.EXCEPTION_FORMAT, - {'method': http_verb, 'url': test_url, 'exc_type_name': ExceptionSubclass.__name__, 'exc_value': expected_exception}, + { + 'method': http_verb, + 'url': test_url, + 'exc_type_name': ExceptionSubclass.__name__, + 'exc_value': expected_exception, + }, ) -def test_network_request_returns_network_response(make_network_request, request_response, network): +def test_network_request_returns_network_response( + make_network_request, request_response, network +): response = make_network_request(network) assert response.request_response is request_response assert isinstance(response, DefaultNetworkResponse) @@ -228,12 +253,19 @@ def test_network_response_only_logs_once(network_response, logger_call_count): @pytest.mark.parametrize('content_length_header', [False, True]) def test_network_logs_successful_json_responses( - construct_network_response, generic_successful_request_response, assert_logger_called_once_with, - get_content_from_response, http_verb, test_url, content_length_header, + construct_network_response, + generic_successful_request_response, + assert_logger_called_once_with, + get_content_from_response, + http_verb, + test_url, + content_length_header, ): expected_content_length = str(len(generic_successful_request_response.content)) if content_length_header: - generic_successful_request_response.headers['Content-Length'] = expected_content_length + generic_successful_request_response.headers['Content-Length'] = ( + expected_content_length + ) else: generic_successful_request_response.headers.pop('Content-Length', None) generic_successful_request_response.request.method = http_verb @@ -250,42 +282,65 @@ def test_network_logs_successful_json_responses( 'content_length': expected_content_length, 'headers': pformat(generic_successful_request_response.headers), 'content': pformat(generic_successful_request_response.json()), - } + }, ) @pytest.mark.parametrize('content_length_header', [False, True]) def test_network_logs_successful_responses_with_file_content_placeholder( - construct_network_response, generic_successful_request_response, assert_logger_called_once_with, - logger, content_length_header, + construct_network_response, + generic_successful_request_response, + assert_logger_called_once_with, + logger, + content_length_header, ): if content_length_header: expected_content_length = str(len(generic_successful_request_response.content)) - generic_successful_request_response.headers['Content-Length'] = expected_content_length + generic_successful_request_response.headers['Content-Length'] = ( + expected_content_length + ) else: generic_successful_request_response.headers.pop('Content-Length', None) expected_content_length = '?' - network_response = construct_network_response(generic_successful_request_response, log_response_content=False) - assert_logger_called_once_with('info', DefaultNetworkResponse.SUCCESSFUL_RESPONSE_FORMAT, ANY) + network_response = construct_network_response( + generic_successful_request_response, log_response_content=False + ) + assert_logger_called_once_with( + 'info', DefaultNetworkResponse.SUCCESSFUL_RESPONSE_FORMAT, ANY + ) assert logger.info.call_args[0][1]['content'] == network_response.CONTENT_NOT_LOGGED assert logger.info.call_args[0][1]['content_length'] == expected_content_length def test_network_logs_successful_responses_with_non_json_content( - construct_network_response, generic_successful_request_response, assert_logger_called_once_with, - logger, get_content_from_response, + construct_network_response, + generic_successful_request_response, + assert_logger_called_once_with, + logger, + get_content_from_response, ): - generic_successful_request_response.content = content = (b''.join(chr(i).encode('utf-8') for i in range(128)) * 4) - generic_successful_request_response.json.side_effect = lambda: json.loads(content.decode('utf-8')) - network_response = construct_network_response(generic_successful_request_response, log_response_content=False) + generic_successful_request_response.content = content = ( + b''.join(chr(i).encode('utf-8') for i in range(128)) * 4 + ) + generic_successful_request_response.json.side_effect = lambda: json.loads( + content.decode('utf-8') + ) + network_response = construct_network_response( + generic_successful_request_response, log_response_content=False + ) get_content_from_response(network_response) - assert_logger_called_once_with('info', DefaultNetworkResponse.SUCCESSFUL_RESPONSE_FORMAT, ANY) + assert_logger_called_once_with( + 'info', DefaultNetworkResponse.SUCCESSFUL_RESPONSE_FORMAT, ANY + ) assert logger.info.call_args[0][1]['content'] == network_response.CONTENT_NOT_LOGGED def test_network_logs_non_successful_responses( - construct_network_response, server_error_request_response, assert_logger_called_once_with, - http_verb, test_url, + construct_network_response, + server_error_request_response, + assert_logger_called_once_with, + http_verb, + test_url, ): server_error_request_response.request.method = http_verb server_error_request_response.request.url = test_url @@ -300,7 +355,7 @@ def test_network_logs_non_successful_responses( 'content_length': str(len(server_error_request_response.content)), 'headers': pformat(server_error_request_response.headers), 'content': pformat(server_error_request_response.json()), - } + }, ) diff --git a/test/unit/object/conftest.py b/test/unit/object/conftest.py index a8c4fbd33..4d64758c6 100644 --- a/test/unit/object/conftest.py +++ b/test/unit/object/conftest.py @@ -32,7 +32,9 @@ from boxsdk.object.terms_of_service_user_status import TermsOfServiceUserStatus from boxsdk.object.collaboration_allowlist import CollaborationAllowlist from boxsdk.object.collaboration_allowlist_entry import CollaborationAllowlistEntry -from boxsdk.object.collaboration_allowlist_exempt_target import CollaborationAllowlistExemptTarget +from boxsdk.object.collaboration_allowlist_exempt_target import ( + CollaborationAllowlistExemptTarget, +) from boxsdk.object.webhook import Webhook from boxsdk.object.task import Task from boxsdk.object.task_assignment import TaskAssignment @@ -69,7 +71,9 @@ def mock_image_path(image_extension): @pytest.fixture(scope='function') def mock_content_response(make_mock_box_request): - mock_box_response, mock_network_response = make_mock_box_request(content=b'Contents of a text file.') + mock_box_response, mock_network_response = make_mock_box_request( + content=b'Contents of a text file.' + ) mock_network_response.response_as_stream = raw = Mock() raw.stream.return_value = (bytes((b,)) for b in mock_box_response.content) return mock_box_response @@ -86,8 +90,14 @@ def mock_upload_response_contains_entries(request): @pytest.fixture(scope='function') -def mock_upload_response(mock_object_id, make_mock_box_request, mock_upload_response_contains_entries): - response = {'type': 'file', 'id': mock_object_id, 'description': 'Test File Description', } +def mock_upload_response( + mock_object_id, make_mock_box_request, mock_upload_response_contains_entries +): + response = { + 'type': 'file', + 'id': mock_object_id, + 'description': 'Test File Description', + } if mock_upload_response_contains_entries: response = {'entries': [response]} mock_box_response, _ = make_mock_box_request(response=response) @@ -369,7 +379,7 @@ def shared_link_password(request): '2018-10-31T00:00:00+14:00', None, SDK_VALUE_NOT_SET, - datetime(2018, 10, 31, 23, 59, 59, tzinfo=pytz.timezone('US/Alaska')) + datetime(2018, 10, 31, 23, 59, 59, tzinfo=pytz.timezone('US/Alaska')), ) ) def shared_link_unshared_at(request): @@ -381,19 +391,14 @@ def shared_link_vanity_name(request): return request.param -@pytest.fixture(params=[ - # Test case for plain message - ( - 'message', - 'Hello there!' - ), - - # Test case for tagged message - ( - 'tagged_message', - '@[22222:Test User] Hi!' - ) -]) +@pytest.fixture( + params=[ + # Test case for plain message + ('message', 'Hello there!'), + # Test case for tagged message + ('tagged_message', '@[22222:Test User] Hi!'), + ] +) def comment_params(request): return request.param diff --git a/test/unit/object/test_base_api_json_object.py b/test/unit/object/test_base_api_json_object.py index a9cf2fb39..79836945e 100644 --- a/test/unit/object/test_base_api_json_object.py +++ b/test/unit/object/test_base_api_json_object.py @@ -37,7 +37,9 @@ def test_iter(base_api_json_object): assert set(all_test_object_keys) == set(all_dictionary_response_keys) -def test_meta_registers_new_item_type_in_default_translator(default_translator, original_default_translator): +def test_meta_registers_new_item_type_in_default_translator( + default_translator, original_default_translator +): item_type = 'ƒøø' class Foo(BaseAPIJSONObject): @@ -49,9 +51,9 @@ class Foo(BaseAPIJSONObject): @pytest.mark.parametrize('subclass', [BaseAPIJSONObject, BaseObject, Folder]) def test_meta_does_not_register_new_subclass_in_default_translator_if_item_type_is_not_defined_in_namespace( - subclass, - default_translator, - original_default_translator, + subclass, + default_translator, + original_default_translator, ): class Foo(subclass): @@ -61,7 +63,9 @@ class Foo(subclass): assert default_translator == original_default_translator -def test_meta_overrides_registration_if_subclass_redefines_item_type(default_translator, original_default_translator): +def test_meta_overrides_registration_if_subclass_redefines_item_type( + default_translator, original_default_translator +): class FolderSubclass(Folder): _item_type = 'folder' diff --git a/test/unit/object/test_base_item.py b/test/unit/object/test_base_item.py index b873b6bfc..4b6031c60 100644 --- a/test/unit/object/test_base_item.py +++ b/test/unit/object/test_base_item.py @@ -4,7 +4,14 @@ @pytest.fixture(params=('file', 'folder', 'web_link')) def test_base_item_and_response( - test_file, test_folder, test_web_link, mock_file_response, mock_folder_response, mock_web_link_response, request): + test_file, + test_folder, + test_web_link, + mock_file_response, + mock_folder_response, + mock_web_link_response, + request, +): if request.param == 'file': return test_file, mock_file_response if request.param == 'folder': @@ -22,9 +29,15 @@ def test_collections_for_addition(mock_collection_id, request): if request.param == 'same': # Adding a second instance of the same collection is handled correctly by the API, # so for simplicity we do not check for an existing copy of the collection and just append - return [{'id': mock_collection_id}], [{'id': mock_collection_id}, {'id': mock_collection_id}] + return [{'id': mock_collection_id}], [ + {'id': mock_collection_id}, + {'id': mock_collection_id}, + ] if request.param == 'other': - return [{'id': other_collection_id}], [{'id': other_collection_id}, {'id': mock_collection_id}] + return [{'id': other_collection_id}], [ + {'id': other_collection_id}, + {'id': mock_collection_id}, + ] raise NotImplementedError(f"Forgot to implement {request.param}") @@ -40,16 +53,25 @@ def test_collections_for_removal(mock_collection_id, request): if request.param == 'only_other': return [{'id': other_collection_id}], [{'id': other_collection_id}] if request.param == 'other_and_removed': - return [{'id': mock_collection_id}, {'id': other_collection_id}], [{'id': other_collection_id}] + return [{'id': mock_collection_id}, {'id': other_collection_id}], [ + {'id': other_collection_id} + ] raise NotImplementedError(f"Forgot to implement {request.param}") -@pytest.mark.parametrize('params, expected_data', [ - ({}, {}), - ({'name': 'New name.pdf'}, {'name': 'New name.pdf'}) -]) -def test_copy_base_item(test_base_item_and_response, mock_box_session, test_folder, mock_object_id, params, expected_data): +@pytest.mark.parametrize( + 'params, expected_data', + [({}, {}), ({'name': 'New name.pdf'}, {'name': 'New name.pdf'})], +) +def test_copy_base_item( + test_base_item_and_response, + mock_box_session, + test_folder, + mock_object_id, + params, + expected_data, +): # pylint:disable=redefined-outer-name, protected-access test_item, mock_item_response = test_base_item_and_response expected_url = test_item.get_url('copy') @@ -59,17 +81,24 @@ def test_copy_base_item(test_base_item_and_response, mock_box_session, test_fold expected_body.update(expected_data) mock_box_session.post.return_value = mock_item_response copy_response = test_item.copy(parent_folder=test_folder, **params) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(copy_response, test_item.__class__) @pytest.mark.parametrize( - 'params, expected_data', [ - ({}, {}), - ({'name': 'New name.pdf'}, {'name': 'New name.pdf'}) - ] + 'params, expected_data', + [({}, {}), ({'name': 'New name.pdf'}, {'name': 'New name.pdf'})], ) -def test_move_base_item(test_base_item_and_response, mock_box_session, test_folder, mock_object_id, params, expected_data): +def test_move_base_item( + test_base_item_and_response, + mock_box_session, + test_folder, + mock_object_id, + params, + expected_data, +): # pylint:disable=redefined-outer-name, protected-access test_item, mock_item_response = test_base_item_and_response expected_url = test_item.get_url() @@ -79,7 +108,9 @@ def test_move_base_item(test_base_item_and_response, mock_box_session, test_fold expected_body.update(expected_data) mock_box_session.put.return_value = mock_item_response move_response = test_item.move(test_folder, **params) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_body), params=None, headers=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(expected_body), params=None, headers=None + ) assert isinstance(move_response, test_item.__class__) @@ -89,19 +120,24 @@ def test_rename_base_item(test_base_item_and_response, mock_box_session): expected_url = test_item.get_url() mock_box_session.put.return_value = mock_item_response rename_response = test_item.rename('new name') - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps({'name': 'new name'}), params=None, headers=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps({'name': 'new name'}), params=None, headers=None + ) assert isinstance(rename_response, test_item.__class__) -def test_add_to_collection(test_base_item_and_response, mock_box_session, mock_collection, test_collections_for_addition): +def test_add_to_collection( + test_base_item_and_response, + mock_box_session, + mock_collection, + test_collections_for_addition, +): # pylint:disable=redefined-outer-name, protected-access test_item, mock_item_response = test_base_item_and_response current_collections, expected_collections = test_collections_for_addition expected_url = test_item.get_url() expected_params = {'fields': 'collections'} - expected_data = { - 'collections': expected_collections - } + expected_data = {'collections': expected_collections} mock_response = { 'type': test_item.object_type, 'id': test_item.object_id, @@ -112,19 +148,26 @@ def test_add_to_collection(test_base_item_and_response, mock_box_session, mock_c test_item.add_to_collection(mock_collection) - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=expected_params) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_data), headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=expected_params + ) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(expected_data), headers=None, params=None + ) -def test_remove_from_collection(test_base_item_and_response, mock_box_session, mock_collection, test_collections_for_removal): +def test_remove_from_collection( + test_base_item_and_response, + mock_box_session, + mock_collection, + test_collections_for_removal, +): # pylint:disable=redefined-outer-name, protected-access test_item, mock_item_response = test_base_item_and_response current_collections, expected_collections = test_collections_for_removal expected_url = test_item.get_url() expected_params = {'fields': 'collections'} - expected_data = { - 'collections': expected_collections - } + expected_data = {'collections': expected_collections} mock_response = { 'type': test_item.object_type, 'id': test_item.object_id, @@ -135,5 +178,9 @@ def test_remove_from_collection(test_base_item_and_response, mock_box_session, m test_item.remove_from_collection(mock_collection) - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=expected_params) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_data), headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=expected_params + ) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(expected_data), headers=None, params=None + ) diff --git a/test/unit/object/test_base_object.py b/test/unit/object/test_base_object.py index ba30afe66..6e3ba13c2 100644 --- a/test/unit/object/test_base_object.py +++ b/test/unit/object/test_base_object.py @@ -10,9 +10,14 @@ @pytest.fixture(params=('file', 'folder', 'user')) def test_object_and_response( - test_file, test_folder, mock_user, - mock_file_response, mock_folder_response, mock_user_response, - request): + test_file, + test_folder, + mock_user, + mock_file_response, + mock_folder_response, + mock_user_response, + request, +): test_objects_and_responses = { 'file': (test_file, mock_file_response), 'folder': (test_folder, mock_folder_response), @@ -21,40 +26,79 @@ def test_object_and_response( return test_objects_and_responses[request.param] -@pytest.fixture(params=('same_file', 'same_folder', 'same_user', 'against_none', 'different_ids', 'different_types')) -def objects_for_comparison(test_file, test_folder, mock_user, request, mock_box_session): +@pytest.fixture( + params=( + 'same_file', + 'same_folder', + 'same_user', + 'against_none', + 'different_ids', + 'different_types', + ) +) +def objects_for_comparison( + test_file, test_folder, mock_user, request, mock_box_session +): cases = { 'identical_object': (test_file, test_file, True), 'same_file': (test_file, File(mock_box_session, test_file.object_id), True), - 'same_folder': (test_folder, Folder(mock_box_session, test_folder.object_id), True), + 'same_folder': ( + test_folder, + Folder(mock_box_session, test_folder.object_id), + True, + ), 'same_user': (mock_user, User(mock_box_session, mock_user.object_id), True), 'against_none': (test_file, None, False), - 'different_ids': (File(mock_box_session, '1'), File(mock_box_session, '2'), False), - 'different_types': (File(mock_box_session, '1'), Folder(mock_box_session, '1'), False), - 'not_api_object': (test_file, {'id': test_file.object_id, 'type': 'file'}, False), + 'different_ids': ( + File(mock_box_session, '1'), + File(mock_box_session, '2'), + False, + ), + 'different_types': ( + File(mock_box_session, '1'), + Folder(mock_box_session, '1'), + False, + ), + 'not_api_object': ( + test_file, + {'id': test_file.object_id, 'type': 'file'}, + False, + ), } return cases[request.param] -@pytest.mark.parametrize('params,headers', product(*([[None, {}, {'foo': 'bar'}, {'foo': 'bar', 'num': 4}]] * 2))) +@pytest.mark.parametrize( + 'params,headers', + product(*([[None, {}, {'foo': 'bar'}, {'foo': 'bar', 'num': 4}]] * 2)), +) def test_update_info(test_object_and_response, mock_box_session, params, headers): # pylint:disable=redefined-outer-name, protected-access test_object, mock_object_response = test_object_and_response expected_url = test_object.get_url() mock_box_session.put.return_value = mock_object_response data = {'foo': 'bar', 'baz': {'foo': 'bar'}, 'num': 4} - update_response = BaseObject.update_info(test_object, data=data, params=params, headers=headers) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), params=params, headers=headers) + update_response = BaseObject.update_info( + test_object, data=data, params=params, headers=headers + ) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), params=params, headers=headers + ) assert isinstance(update_response, test_object.__class__) assert update_response.object_id == test_object.object_id -@pytest.mark.parametrize('params, headers, success', [ - (None, None, True), - ({'a': 'b'}, {'10': '20'}, True), - ({'a': 'b'}, None, False), -]) -def test_delete_handles_params_and_headers_correctly(mock_box_session, make_mock_box_request, params, headers, success): +@pytest.mark.parametrize( + 'params, headers, success', + [ + (None, None, True), + ({'a': 'b'}, {'10': '20'}, True), + ({'a': 'b'}, None, False), + ], +) +def test_delete_handles_params_and_headers_correctly( + mock_box_session, make_mock_box_request, params, headers, success +): # pylint:disable=redefined-outer-name, protected-access fake_id = 'a_fake_id' base_object = BaseObject(mock_box_session, fake_id) @@ -78,7 +122,9 @@ def test_getattr_and_getitem(test_object_and_response, mock_box_session): mock_box_session.put.return_value = mock_object_response update_response = BaseObject.update_info(test_object, data={}) assert isinstance(update_response, test_object.__class__) - assert update_response.object_id == update_response.id == update_response['id'] # pylint:disable=no-member + assert ( + update_response.object_id == update_response.id == update_response['id'] + ) # pylint:disable=no-member def test_get_url(test_object_and_response): diff --git a/test/unit/object/test_chunked_upload.py b/test/unit/object/test_chunked_upload.py index 67984d523..7c7d9c0ad 100644 --- a/test/unit/object/test_chunked_upload.py +++ b/test/unit/object/test_chunked_upload.py @@ -10,7 +10,9 @@ from boxsdk.exception import BoxAPIException from boxsdk.exception import BoxException from boxsdk.object.file import File -from boxsdk.pagination.limit_offset_based_dict_collection import LimitOffsetBasedDictCollection +from boxsdk.pagination.limit_offset_based_dict_collection import ( + LimitOffsetBasedDictCollection, +) from boxsdk.object.upload_session import UploadSession from boxsdk.util.chunked_uploader import ChunkedUploader @@ -23,9 +25,13 @@ def test_upload_session(mock_box_session): 'session_expires_at': '2017-11-09T21:59:16Z', 'id': 'D5E3F7ADA11A38A0A66AD0B64AACA658', 'type': 'upload_session', - 'num_parts_processed': 0 + 'num_parts_processed': 0, } - return UploadSession(mock_box_session, 'D5E3F7ADA11A38A0A66AD0B64AACA658', upload_session_response_object) + return UploadSession( + mock_box_session, + 'D5E3F7ADA11A38A0A66AD0B64AACA658', + upload_session_response_object, + ) @pytest.fixture() @@ -40,8 +46,12 @@ def mock_upload_session(): def test_start(test_upload_session, mock_box_session): - expected_put_url = f'{API.UPLOAD_URL}/files/upload_sessions/{test_upload_session.object_id}' - expected_post_url = f'{API.UPLOAD_URL}/files/upload_sessions/{test_upload_session.object_id}/commit' + expected_put_url = ( + f'{API.UPLOAD_URL}/files/upload_sessions/{test_upload_session.object_id}' + ) + expected_post_url = ( + f'{API.UPLOAD_URL}/files/upload_sessions/{test_upload_session.object_id}/commit' + ) file_size = 7 first_sha1 = '2iNhTgJGmg18e9G9q1ycR0sZBNw=' second_sha1 = 'A0d4GYoEXB7YC+JxzdApt2h09vw=' @@ -106,23 +116,15 @@ def test_start(test_upload_session, mock_box_session): third_response_mock = Mock() fourth_response_mock = Mock() - first_response_mock.json.return_value = { - 'part': part_one - } - second_response_mock.json.return_value = { - 'part': part_two - } - third_response_mock.json.return_value = { - 'part': part_three - } - fourth_response_mock.json.return_value = { - 'part': part_four - } + first_response_mock.json.return_value = {'part': part_one} + second_response_mock.json.return_value = {'part': part_two} + third_response_mock.json.return_value = {'part': part_three} + fourth_response_mock.json.return_value = {'part': part_four} mock_box_session.put.side_effect = [ first_response_mock, second_response_mock, third_response_mock, - fourth_response_mock + fourth_response_mock, ] mock_box_session.post.return_value.json.return_value = { 'entries': [ @@ -144,12 +146,12 @@ def test_start(test_upload_session, mock_box_session): call(expected_put_url, data=b'ef', headers=expected_headers_third_upload), call(expected_put_url, data=b'g', headers=expected_headers_fourth_upload), ] - flaky_stream.read.assert_has_calls([call(2), call(2), call(2), call(1), call(2), call(2), call(1)], any_order=False) + flaky_stream.read.assert_has_calls( + [call(2), call(2), call(2), call(1), call(2), call(2), call(1)], any_order=False + ) mock_box_session.put.assert_has_calls(calls, any_order=True) mock_box_session.post.assert_called_once_with( - expected_post_url, - data=json.dumps(expected_data), - headers=expected_headers + expected_post_url, data=json.dumps(expected_data), headers=expected_headers ) assert uploaded_file.type == 'file' assert uploaded_file.id == '12345' @@ -200,8 +202,7 @@ def test_resume_cross_process(test_file, mock_upload_session): ] mock_upload_session.upload_part_bytes.assert_has_calls(calls, any_order=True) mock_upload_session.commit.assert_called_once_with( - content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', - parts=parts + content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', parts=parts ) assert uploaded_file is test_file @@ -244,7 +245,7 @@ def test_resume_in_process(test_file, mock_upload_session): first_part, second_part, BoxAPIException(502), - fourth_part + fourth_part, ] uploaded_file = None calls = [ @@ -261,8 +262,7 @@ def test_resume_in_process(test_file, mock_upload_session): uploaded_file = chunked_uploader.resume() mock_upload_session.upload_part_bytes.assert_has_calls(calls, any_order=True) mock_upload_session.commit.assert_called_once_with( - content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', - parts=parts + content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', parts=parts ) assert uploaded_file is test_file @@ -314,7 +314,7 @@ def upload_mock_func(part_bytes, offset, total_size): second_part, BoxAPIException(502), fourth_part, - third_part + third_part, ] mock_upload_session.upload_part_bytes.side_effect = upload_mock_func uploaded_file = None @@ -331,8 +331,7 @@ def upload_mock_func(part_bytes, offset, total_size): ] mock_upload_session.upload_part_bytes.assert_has_calls(calls, any_order=True) mock_upload_session.commit.assert_called_once_with( - content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', - parts=parts + content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', parts=parts ) assert uploaded_file is test_file @@ -398,7 +397,12 @@ def test_resume_after_commit_failed(test_file, mock_upload_session): } parts = [first_part, second_part, third_part, fourth_part] mock_iterator = MagicMock(LimitOffsetBasedDictCollection) - mock_iterator.__iter__.return_value = [first_part, second_part, third_part, fourth_part] + mock_iterator.__iter__.return_value = [ + first_part, + second_part, + third_part, + fourth_part, + ] mock_upload_session.get_parts.return_value = mock_iterator mock_upload_session.commit.side_effect = [None, test_file] chunked_uploader = ChunkedUploader(mock_upload_session, stream, file_size) @@ -406,7 +410,7 @@ def test_resume_after_commit_failed(test_file, mock_upload_session): first_part, second_part, third_part, - fourth_part + fourth_part, ] # when @@ -420,8 +424,7 @@ def test_resume_after_commit_failed(test_file, mock_upload_session): # then expected_call = call( - content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', - parts=parts + content_sha1=b'/\xb5\xe14\x19\xfc\x89$he\xe7\xa3$\xf4v\xecbN\x87@', parts=parts ) mock_upload_session.commit.assert_has_calls([expected_call, expected_call]) assert uploaded_file is test_file diff --git a/test/unit/object/test_collaboration.py b/test/unit/object/test_collaboration.py index c461a86e9..cb57cd0e0 100644 --- a/test/unit/object/test_collaboration.py +++ b/test/unit/object/test_collaboration.py @@ -5,19 +5,20 @@ from boxsdk.object.collaboration import CollaborationRole, CollaborationStatus -@pytest.mark.parametrize('data', [ - {}, - {'role': CollaborationRole.EDITOR}, - {'role': CollaborationRole.VIEWER}, - {'status': CollaborationStatus.ACCEPTED}, - {'status': CollaborationStatus.REJECTED}, - {'role': CollaborationRole.EDITOR, 'status': CollaborationStatus.ACCEPTED}, -]) +@pytest.mark.parametrize( + 'data', + [ + {}, + {'role': CollaborationRole.EDITOR}, + {'role': CollaborationRole.VIEWER}, + {'status': CollaborationStatus.ACCEPTED}, + {'status': CollaborationStatus.REJECTED}, + {'role': CollaborationRole.EDITOR, 'status': CollaborationStatus.ACCEPTED}, + ], +) def test_update_info_returns_the_correct_response( - test_collaboration, - mock_box_session, - mock_collab_response, - data): + test_collaboration, mock_box_session, mock_collab_response, data +): # pylint:disable=protected-access expected_url = test_collaboration.get_url() mock_box_session.put.return_value = mock_collab_response @@ -32,18 +33,19 @@ def test_update_info_returns_the_correct_response( assert update_response.object_id == test_collaboration.object_id -@pytest.mark.parametrize('data', [ - {}, - {'role': CollaborationRole.EDITOR, 'status': CollaborationStatus.REJECTED}, - {'role': CollaborationRole.EDITOR, 'status': CollaborationStatus.ACCEPTED}, - {'role': CollaborationRole.EDITOR, 'expires_at': '2025-08-29T23:59:00-07:00'}, - {'role': CollaborationRole.EDITOR, 'can_view_path': True}, -]) +@pytest.mark.parametrize( + 'data', + [ + {}, + {'role': CollaborationRole.EDITOR, 'status': CollaborationStatus.REJECTED}, + {'role': CollaborationRole.EDITOR, 'status': CollaborationStatus.ACCEPTED}, + {'role': CollaborationRole.EDITOR, 'expires_at': '2025-08-29T23:59:00-07:00'}, + {'role': CollaborationRole.EDITOR, 'can_view_path': True}, + ], +) def test_update_info_returns_the_correct_response_with_data_param( - test_collaboration, - mock_box_session, - mock_collab_response, - data): + test_collaboration, mock_box_session, mock_collab_response, data +): # pylint:disable=protected-access expected_url = test_collaboration.get_url() mock_box_session.put.return_value = mock_collab_response @@ -58,9 +60,7 @@ def test_update_info_returns_the_correct_response_with_data_param( assert update_response.object_id == test_collaboration.object_id -def test_update_info_returns_204( - test_collaboration, - mock_box_session): +def test_update_info_returns_204(test_collaboration, mock_box_session): # pylint:disable=protected-access data = {'role': CollaborationRole.OWNER, 'status': CollaborationStatus.ACCEPTED} expected_url = test_collaboration.get_url() @@ -87,9 +87,7 @@ def test_accept_pending_collaboration(test_collaboration, mock_box_session): } mock_box_session.put.return_value.json.return_value = mock_collab_response response = test_collaboration.accept() - update_body = { - 'status': 'accepted' - } + update_body = {'status': 'accepted'} mock_box_session.put.assert_called_once_with( expected_url, data=json.dumps(update_body), @@ -111,9 +109,7 @@ def test_reject_pending_collaboration(test_collaboration, mock_box_session): } mock_box_session.put.return_value.json.return_value = mock_collab_response response = test_collaboration.reject() - update_body = { - 'status': 'rejected' - } + update_body = {'status': 'rejected'} mock_box_session.put.assert_called_once_with( expected_url, data=json.dumps(update_body), diff --git a/test/unit/object/test_collaboration_allowlist.py b/test/unit/object/test_collaboration_allowlist.py index 2cebeee70..7d378ff22 100644 --- a/test/unit/object/test_collaboration_allowlist.py +++ b/test/unit/object/test_collaboration_allowlist.py @@ -3,7 +3,9 @@ from boxsdk.config import API from boxsdk.object.collaboration_allowlist_entry import CollaborationAllowlistEntry -from boxsdk.object.collaboration_allowlist_exempt_target import CollaborationAllowlistExemptTarget +from boxsdk.object.collaboration_allowlist_exempt_target import ( + CollaborationAllowlistExemptTarget, +) def test_get_entries(mock_box_session, test_collaboration_allowlist): @@ -12,11 +14,11 @@ def test_get_entries(mock_box_session, test_collaboration_allowlist): 'type': 'collaboration_whitelist_entry', 'id': '12345', 'domain': 'box.com', - 'direction': 'both' + 'direction': 'both', } mock_box_session.get.return_value.json.return_value = { 'limit': 100, - 'entries': [mock_entry] + 'entries': [mock_entry], } entries = test_collaboration_allowlist.get_entries() entry = entries.next() @@ -32,14 +34,11 @@ def test_get_exemptions(mock_box_session, test_collaboration_allowlist): mock_exemption = { 'type': 'collaboration_whitelist_exempt_target', 'id': '12345', - 'user': { - 'type': 'user', - 'id': '33333' - } + 'user': {'type': 'user', 'id': '33333'}, } mock_box_session.get.return_value.json.return_value = { 'limit': 100, - 'entries': [mock_exemption] + 'entries': [mock_exemption], } exemptions = test_collaboration_allowlist.get_exemptions() exemption = exemptions.next() @@ -49,10 +48,7 @@ def test_get_exemptions(mock_box_session, test_collaboration_allowlist): assert exemption.user['id'] == mock_exemption['user']['id'] -@pytest.mark.parametrize( - 'direction', - ['inbound', 'outbound', 'both'] -) +@pytest.mark.parametrize('direction', ['inbound', 'outbound', 'both']) def test_add_domain(mock_box_session, test_collaboration_allowlist, direction): expected_url = f'{API.BASE_API_URL}/collaboration_whitelist_entries' domain = 'example.com' @@ -60,15 +56,14 @@ def test_add_domain(mock_box_session, test_collaboration_allowlist, direction): 'type': 'collaboration_whitelist_entry', 'id': '12345', 'domain': domain, - 'direction': direction - } - expected_data = { - 'domain': domain, - 'direction': direction + 'direction': direction, } + expected_data = {'domain': domain, 'direction': direction} mock_box_session.post.return_value.json.return_value = mock_entry entry = test_collaboration_allowlist.add_domain(domain, direction) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert isinstance(entry, CollaborationAllowlistEntry) assert entry.id == mock_entry['id'] assert entry.domain == domain @@ -77,21 +72,16 @@ def test_add_domain(mock_box_session, test_collaboration_allowlist, direction): def test_add_exemption(mock_box_session, test_collaboration_allowlist, mock_user): expected_url = f'{API.BASE_API_URL}/collaboration_whitelist_exempt_targets' - expected_data = { - 'user': { - 'id': mock_user.object_id - } - } + expected_data = {'user': {'id': mock_user.object_id}} mock_exemption = { 'type': 'collaboration_whitelist_exempt_target', 'id': '12345', - 'user': { - 'type': 'user', - 'id': mock_user.object_id - } + 'user': {'type': 'user', 'id': mock_user.object_id}, } mock_box_session.post.return_value.json.return_value = mock_exemption exemption = test_collaboration_allowlist.add_exemption(mock_user) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert exemption.id == mock_exemption['id'] assert exemption.user['id'] == mock_exemption['user']['id'] diff --git a/test/unit/object/test_collaboration_allowlist_entry.py b/test/unit/object/test_collaboration_allowlist_entry.py index e3c24267c..da3d7552f 100644 --- a/test/unit/object/test_collaboration_allowlist_entry.py +++ b/test/unit/object/test_collaboration_allowlist_entry.py @@ -8,11 +8,13 @@ def test_get(mock_box_session, test_collaboration_allowlist_entry): 'type': 'collaboration_whitelist_entry', 'id': '98765', 'domain': 'example.com', - 'direction': 'inbound' + 'direction': 'inbound', } mock_box_session.get.return_value.json.return_value = mock_entry entry = test_collaboration_allowlist_entry.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert entry.id == mock_entry['id'] assert entry.domain == mock_entry['domain'] assert entry.direction == mock_entry['direction'] @@ -22,4 +24,6 @@ def test_delete(mock_box_session, test_collaboration_allowlist_entry): entry_id = test_collaboration_allowlist_entry.object_id expected_url = f'{API.BASE_API_URL}/collaboration_whitelist_entries/{entry_id}' test_collaboration_allowlist_entry.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={}) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=None, params={} + ) diff --git a/test/unit/object/test_collaboration_allowlist_exempt_target.py b/test/unit/object/test_collaboration_allowlist_exempt_target.py index aa587f008..a7bc7a072 100644 --- a/test/unit/object/test_collaboration_allowlist_exempt_target.py +++ b/test/unit/object/test_collaboration_allowlist_exempt_target.py @@ -3,16 +3,20 @@ def test_get(mock_box_session, test_collaboration_allowlist_exemption): exemption_id = test_collaboration_allowlist_exemption.object_id - expected_url = f'{API.BASE_API_URL}/collaboration_whitelist_exempt_targets/{exemption_id}' + expected_url = ( + f'{API.BASE_API_URL}/collaboration_whitelist_exempt_targets/{exemption_id}' + ) mock_exemption = { 'type': 'collaboration_whitelist_entry', 'id': '98765', 'domain': 'example.com', - 'direction': 'inbound' + 'direction': 'inbound', } mock_box_session.get.return_value.json.return_value = mock_exemption exemption = test_collaboration_allowlist_exemption.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert exemption.id == mock_exemption['id'] assert exemption.domain == mock_exemption['domain'] assert exemption.direction == mock_exemption['direction'] @@ -20,6 +24,10 @@ def test_get(mock_box_session, test_collaboration_allowlist_exemption): def test_delete(mock_box_session, test_collaboration_allowlist_exemption): exemption_id = test_collaboration_allowlist_exemption.object_id - expected_url = mock_box_session.get_url('collaboration_whitelist_exempt_targets', exemption_id) + expected_url = mock_box_session.get_url( + 'collaboration_whitelist_exempt_targets', exemption_id + ) test_collaboration_allowlist_exemption.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={}) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=None, params={} + ) diff --git a/test/unit/object/test_collection.py b/test/unit/object/test_collection.py index 3c13fbe13..08c02d794 100644 --- a/test/unit/object/test_collection.py +++ b/test/unit/object/test_collection.py @@ -6,12 +6,14 @@ def test_get(mock_collection, mock_box_session): 'type': 'collection', 'id': mock_collection.object_id, 'name': collection_name, - 'collection_type': 'favorites' + 'collection_type': 'favorites', } fetched_collection = mock_collection.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert fetched_collection.name == collection_name @@ -25,15 +27,9 @@ def test_get_items(mock_collection, mock_box_session): 'offset': 0, 'total_count': 2, 'entries': [ - { - 'type': 'folder', - 'id': item_id1 - }, - { - 'type': 'file', - 'id': item_id2 - } - ] + {'type': 'folder', 'id': item_id1}, + {'type': 'file', 'id': item_id2}, + ], } items = mock_collection.get_items() diff --git a/test/unit/object/test_comment.py b/test/unit/object/test_comment.py index a078dd6a8..b04f62c84 100644 --- a/test/unit/object/test_comment.py +++ b/test/unit/object/test_comment.py @@ -2,27 +2,26 @@ from boxsdk.object.comment import Comment - # pylint:disable=protected-access # pylint:disable=redefined-outer-name + def test_reply(test_comment, mock_box_session, comment_params): expected_url = mock_box_session.get_url('comments') message_type, message = comment_params expected_data = { message_type: message, - 'item': { - 'type': 'comment', - 'id': test_comment.object_id - } + 'item': {'type': 'comment', 'id': test_comment.object_id}, } mock_box_session.post.return_value.json.return_value = { 'type': 'comment', 'id': '12345', - message_type: message + message_type: message, } reply_comment = test_comment.reply(message) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert isinstance(reply_comment, Comment) assert reply_comment.object_id == '12345' @@ -36,10 +35,12 @@ def test_edit(test_comment, mock_box_session, comment_params): mock_box_session.put.return_value.json.return_value = { 'type': 'comment', 'id': '12345', - message_type: message + message_type: message, } updated_comment = test_comment.edit(message) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(expected_data), headers=None, params=None + ) assert isinstance(updated_comment, Comment) assert updated_comment[message_type] == message @@ -50,10 +51,12 @@ def test_get(mock_box_session): mock_box_session.get.return_value.json.return_value = { 'type': 'comment', 'id': comment_id, - 'message': 'Hi!' + 'message': 'Hi!', } comment = Comment(mock_box_session, comment_id).get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert comment.object_id == comment_id assert comment.message == 'Hi!' # pylint:disable=no-member @@ -61,4 +64,6 @@ def test_get(mock_box_session): def test_delete(test_comment, mock_box_session): expected_url = test_comment.get_url() test_comment.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={}) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=None, params={} + ) diff --git a/test/unit/object/test_device_pin.py b/test/unit/object/test_device_pin.py index 001fd6876..88f8028a7 100644 --- a/test/unit/object/test_device_pin.py +++ b/test/unit/object/test_device_pin.py @@ -20,18 +20,20 @@ def test_get(test_device_pin, mock_box_session): mock_box_session.get.return_value.json.return_value = { 'type': 'device_pinner', 'id': test_device_pin.object_id, - 'created_at': created_at + 'created_at': created_at, } device_pin = test_device_pin.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(device_pin, DevicePinner) assert device_pin.created_at == created_at def test_delete_device_pin_return_the_correct_response( - test_device_pin, - mock_box_session, - delete_device_pin_response, + test_device_pin, + mock_box_session, + delete_device_pin_response, ): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value = delete_device_pin_response @@ -39,5 +41,7 @@ def test_delete_device_pin_return_the_correct_response( # pylint:disable=protected-access expected_url = test_device_pin.get_url() # pylint:enable = protected-access - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True diff --git a/test/unit/object/test_enterprise.py b/test/unit/object/test_enterprise.py index 5f063c9af..b2347388b 100644 --- a/test/unit/object/test_enterprise.py +++ b/test/unit/object/test_enterprise.py @@ -18,14 +18,16 @@ def test_invite_user(test_enterprise, mock_box_session): # pylint:disable=redefined-outer-name expected_url = f'{API.BASE_API_URL}/invites' test_user_login = 'test@user.com' - expected_body = json.dumps({ - 'enterprise': { - 'id': test_enterprise.object_id, - }, - 'actionable_by': { - 'login': test_user_login, - }, - }) + expected_body = json.dumps( + { + 'enterprise': { + 'id': test_enterprise.object_id, + }, + 'actionable_by': { + 'login': test_user_login, + }, + } + ) invite_json = { 'type': 'invite', 'id': '11111', diff --git a/test/unit/object/test_event.py b/test/unit/object/test_event.py index a006c7433..2dff35a74 100644 --- a/test/unit/object/test_event.py +++ b/test/unit/object/test_event.py @@ -6,11 +6,11 @@ def test_init_event(): { "type": "event", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", - "source": - { + "source": { "type": "folder", "id": "11446498", }, - }) + } + ) assert event['type'] == 'event' assert event['event_id'] == 'f82c3ba03e41f7e8a7608363cc6c0390183c3f83' diff --git a/test/unit/object/test_events.py b/test/unit/object/test_events.py index 4dde702c0..67fd283aa 100644 --- a/test/unit/object/test_events.py +++ b/test/unit/object/test_events.py @@ -52,15 +52,16 @@ def test_events_stream_type_extended_enum_class_has_expected_members(): @pytest.fixture( scope='session', - params=list(chain( - [None], # Default behavior of not passing any stream_type - STREAM_TYPES_AS_ENUM_INSTANCES, # Passing an enum instance - STREAM_TYPES_AS_STRINGS, # Passing an enum value - - # For forwards compatibility, make sure that it works to pass a string - # value that is not a member of the enum. - ['future_stream_type'], - )), + params=list( + chain( + [None], # Default behavior of not passing any stream_type + STREAM_TYPES_AS_ENUM_INSTANCES, # Passing an enum instance + STREAM_TYPES_AS_STRINGS, # Passing an enum value + # For forwards compatibility, make sure that it works to pass a string + # value that is not a member of the enum. + ['future_stream_type'], + ) + ), ) def stream_type_param(request) -> Optional[Union[str, EventsStreamType]]: """The value to pass as an Event method's stream_type parameter. @@ -73,8 +74,7 @@ def stream_type_param(request) -> Optional[Union[str, EventsStreamType]]: @pytest.fixture() def expected_stream_type(stream_type_param) -> str: - """The stream type we expect to use. - """ + """The stream type we expect to use.""" if stream_type_param is None: return UserEventsStreamType.ALL return stream_type_param @@ -82,8 +82,7 @@ def expected_stream_type(stream_type_param) -> str: @pytest.fixture() def stream_type_kwargs(stream_type_param) -> dict: - """The kwargs for stream_type to pass when invoking a method on `Events`. - """ + """The kwargs for stream_type to pass when invoking a method on `Events`.""" if stream_type_param: return {'stream_type': stream_type_param} return {} @@ -91,8 +90,7 @@ def stream_type_kwargs(stream_type_param) -> dict: @pytest.fixture() def expected_stream_type_params(expected_stream_type) -> OrderedDict: - """The stream_type-related params that we expect to pass to request methods. - """ + """The stream_type-related params that we expect to pass to request methods.""" return OrderedDict(stream_type=expected_stream_type) @@ -102,7 +100,10 @@ def empty_events_response(final_stream_position): mock_box_response = Mock(BoxResponse) mock_network_response = Mock(DefaultNetworkResponse) mock_box_response.network_response = mock_network_response - mock_box_response.json.return_value = mock_json = {'next_stream_position': final_stream_position, 'entries': []} + mock_box_response.json.return_value = mock_json = { + 'next_stream_position': final_stream_position, + 'entries': [], + } mock_box_response.content = json.dumps(mock_json).encode() mock_box_response.status_code = 200 mock_box_response.ok = True @@ -173,17 +174,20 @@ def mock_event_json(): def events_response(initial_stream_position, mock_event_json, make_mock_box_request): # pylint:disable=redefined-outer-name mock_box_response, _ = make_mock_box_request( - response={"next_stream_position": initial_stream_position, "entries": [mock_event_json]}, + response={ + "next_stream_position": initial_stream_position, + "entries": [mock_event_json], + }, ) return mock_box_response def test_get_events( - test_events, - mock_box_session, - events_response, - stream_type_kwargs, - expected_stream_type_params, + test_events, + mock_box_session, + events_response, + stream_type_kwargs, + expected_stream_type_params, ): # pylint:disable=redefined-outer-name expected_url = test_events.get_url() @@ -194,27 +198,30 @@ def test_get_events( expected_url, params={'limit': 100, 'stream_position': 0, **expected_stream_type_params}, ) - for event, json in zip(events['entries'], events_response.json.return_value['entries']): + for event, json in zip( + events['entries'], events_response.json.return_value['entries'] + ): assert isinstance(event, Event) assert event.event_id == json['event_id'] @pytest.mark.parametrize("limit", [100, None]) -@pytest.mark.parametrize('created_after', [ - '2019-07-01T22:02:24+14:00', - datetime(2019, 7, 1, 22, 2, 24, tzinfo=pytz.timezone('US/Alaska')) -]) -@pytest.mark.parametrize('created_before', [ - '2019-08-07T22:02:24+14:00', - datetime(2019, 8, 7, 22, 2, 24, tzinfo=pytz.timezone('US/Alaska')) -]) +@pytest.mark.parametrize( + 'created_after', + [ + '2019-07-01T22:02:24+14:00', + datetime(2019, 7, 1, 22, 2, 24, tzinfo=pytz.timezone('US/Alaska')), + ], +) +@pytest.mark.parametrize( + 'created_before', + [ + '2019-08-07T22:02:24+14:00', + datetime(2019, 8, 7, 22, 2, 24, tzinfo=pytz.timezone('US/Alaska')), + ], +) def test_get_admin_events( - test_events, - mock_box_session, - events_response, - limit, - created_after, - created_before + test_events, mock_box_session, events_response, limit, created_after, created_before ): # pylint:disable=redefined-outer-name expected_url = test_events.get_url() @@ -231,26 +238,25 @@ def test_get_admin_events( 'created_after': '2019-07-01T22:02:24+14:00', 'created_before': '2019-08-07T22:02:24+14:00', 'event_type': 'ITEM_CREATE,LOGIN', - 'stream_type': 'admin_logs' + 'stream_type': 'admin_logs', } if limit: expected_params['limit'] = limit - mock_box_session.get.assert_called_with( - expected_url, - params=expected_params - ) - for event, json in zip(events['entries'], events_response.json.return_value['entries']): + mock_box_session.get.assert_called_with(expected_url, params=expected_params) + for event, json in zip( + events['entries'], events_response.json.return_value['entries'] + ): assert isinstance(event, Event) assert event.event_id == json['event_id'] @pytest.mark.parametrize("limit", [100, None]) def test_get_admin_events_streaming( - test_events, - mock_box_session, - events_response, - limit, + test_events, + mock_box_session, + events_response, + limit, ): # pylint:disable=redefined-outer-name expected_url = test_events.get_url() @@ -263,51 +269,52 @@ def test_get_admin_events_streaming( expected_params = { 'stream_type': 'admin_logs_streaming', 'stream_position': 100, - 'event_type': 'ITEM_CREATE,LOGIN' + 'event_type': 'ITEM_CREATE,LOGIN', } if limit: expected_params['limit'] = limit - mock_box_session.get.assert_called_with( - expected_url, - params=expected_params - ) - for event, json in zip(events['entries'], events_response.json.return_value['entries']): + mock_box_session.get.assert_called_with(expected_url, params=expected_params) + for event, json in zip( + events['entries'], events_response.json.return_value['entries'] + ): assert isinstance(event, Event) assert event.event_id == json['event_id'] def test_get_long_poll_options( - mock_box_session, - test_events, - stream_type_kwargs, - expected_stream_type_params, - options_response, - options_response_entry, + mock_box_session, + test_events, + stream_type_kwargs, + expected_stream_type_params, + options_response, + options_response_entry, ): expected_url = test_events.get_url() mock_box_session.options.return_value = options_response long_poll_options = test_events.get_long_poll_options(**stream_type_kwargs) - mock_box_session.options.assert_called_with(expected_url, params=expected_stream_type_params) + mock_box_session.options.assert_called_with( + expected_url, params=expected_stream_type_params + ) assert long_poll_options == options_response_entry def test_generate_events_with_long_polling( - test_events, - mock_box_session, - events_response, - empty_events_response, - initial_stream_position, - long_poll_url, - retry_timeout, - options_response, - new_change_long_poll_response, - reconnect_long_poll_response, - max_retries_long_poll_response, - mock_event_json, - stream_type_kwargs, - expected_stream_type, - expected_stream_type_params, + test_events, + mock_box_session, + events_response, + empty_events_response, + initial_stream_position, + long_poll_url, + retry_timeout, + options_response, + new_change_long_poll_response, + reconnect_long_poll_response, + max_retries_long_poll_response, + mock_event_json, + stream_type_kwargs, + expected_stream_type, + expected_stream_type_params, ): # pylint:disable=redefined-outer-name expected_url = test_events.get_url() @@ -321,7 +328,9 @@ def test_generate_events_with_long_polling( events_response, new_change_long_poll_response, empty_events_response, - EscapeGenerator("A fake exception for the session to throw so that the generator won't block forever"), + EscapeGenerator( + "A fake exception for the session to throw so that the generator won't block forever" + ), ] events = test_events.generate_events_with_long_polling(**stream_type_kwargs) @@ -333,12 +342,25 @@ def test_generate_events_with_long_polling( with pytest.raises(EscapeGenerator): next(events) events.close() - mock_box_session.options.assert_called_with(expected_url, params=expected_stream_type_params) - mock_box_session.get.assert_any_call(expected_url, params={'stream_position': 'now', 'limit': 0, 'stream_type': expected_stream_type}) + mock_box_session.options.assert_called_with( + expected_url, params=expected_stream_type_params + ) + mock_box_session.get.assert_any_call( + expected_url, + params={ + 'stream_position': 'now', + 'limit': 0, + 'stream_type': expected_stream_type, + }, + ) assert '/events' in expected_url mock_box_session.get.assert_any_call( expected_url, - params={'limit': 100, 'stream_position': initial_stream_position, **expected_stream_type_params}, + params={ + 'limit': 100, + 'stream_position': initial_stream_position, + **expected_stream_type_params, + }, ) mock_box_session.get.assert_any_call( long_poll_url, diff --git a/test/unit/object/test_file.py b/test/unit/object/test_file.py index 272ad0ade..c55608bdc 100644 --- a/test/unit/object/test_file.py +++ b/test/unit/object/test_file.py @@ -18,18 +18,20 @@ from boxsdk.util.datetime_formatter import normalize_date_to_rfc3339_format from boxsdk.util.default_arg_value import SDK_VALUE_NOT_SET - # pylint:disable=protected-access # pylint:disable=too-many-lines # pylint:disable=redefined-outer-name + @pytest.fixture() def mock_accelerator_upload_url_for_update(): return 'https://upload.box.com/api/2.0/files/fake_file_id/content?upload_session_id=123' @pytest.fixture(scope='function') -def mock_accelerator_response_for_update(make_mock_box_request, mock_accelerator_upload_url_for_update): +def mock_accelerator_response_for_update( + make_mock_box_request, mock_accelerator_upload_url_for_update +): mock_response, _ = make_mock_box_request( response={ 'upload_url': mock_accelerator_upload_url_for_update, @@ -63,7 +65,7 @@ def test_create_upload_session(test_file, mock_box_session, use_upload_session_u expected_data = { 'file_id': test_file.object_id, 'file_size': file_size, - 'file_name': file_name + 'file_name': file_name, } mock_box_session.post.return_value.json.return_value = { 'id': upload_session_id, @@ -75,7 +77,9 @@ def test_create_upload_session(test_file, mock_box_session, use_upload_session_u upload_session = test_file.create_upload_session( file_size, file_name, use_upload_session_urls=use_upload_session_urls ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert isinstance(upload_session, UploadSession) assert upload_session._session == mock_box_session assert upload_session.part_size == part_size @@ -88,7 +92,11 @@ def test_create_upload_session(test_file, mock_box_session, use_upload_session_u @pytest.mark.parametrize('use_upload_session_urls', [True, False]) def test_get_chunked_uploader( - mock_box_session, mock_content_response, mock_file_path, test_file, use_upload_session_urls + mock_box_session, + mock_content_response, + mock_file_path, + test_file, + use_upload_session_urls, ): expected_url = f'{API.UPLOAD_URL}/files/{test_file.object_id}/upload_sessions' mock_file_stream = BytesIO(mock_content_response.content) @@ -115,7 +123,9 @@ def test_get_chunked_uploader( chunked_uploader = test_file.get_chunked_uploader( mock_file_path, use_upload_session_urls=use_upload_session_urls ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) upload_session = chunked_uploader._upload_session assert upload_session.part_size == part_size assert upload_session.total_parts == total_parts @@ -130,8 +140,8 @@ def test_get_chunked_uploader( 'due_at', [ '2014-04-03T11:09:43+14:00', - datetime(2014, 4, 3, 11, 9, 43, tzinfo=pytz.timezone('US/Alaska')) - ] + datetime(2014, 4, 3, 11, 9, 43, tzinfo=pytz.timezone('US/Alaska')), + ], ) def test_create_task(test_file, test_task, mock_box_session, due_at): # pylint:disable=redefined-outer-name @@ -155,9 +165,11 @@ def test_create_task(test_file, test_task, mock_box_session, due_at): }, 'action': action, 'message': message, - 'due_at': '2014-04-03T11:09:43+14:00' + 'due_at': '2014-04-03T11:09:43+14:00', } - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(new_task, Task) assert new_task.object_type == test_task.object_type assert new_task.object_id == test_task.object_id @@ -234,15 +246,10 @@ def test_get_tasks(test_file, mock_box_session): def test_get_download_url(test_file, mock_box_session): expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}/content' download_url = 'https://dl.boxcloud.com/sdjhfgksdjfgshdbg' - mock_box_session.get.return_value.headers = { - 'location': download_url - } + mock_box_session.get.return_value.headers = {'location': download_url} url = test_file.get_download_url() mock_box_session.get.assert_called_once_with( - expected_url, - params=None, - expect_json_response=False, - allow_redirects=False + expected_url, params=None, expect_json_response=False, allow_redirects=False ) assert url == download_url @@ -254,35 +261,40 @@ def test_get_download_url_failed(test_file, mock_box_session): test_file.get_download_url() assert exc_info.value.message == 'Download URL is not present in the response.' mock_box_session.get.assert_called_once_with( - expected_url, - params=None, - expect_json_response=False, - allow_redirects=False + expected_url, params=None, expect_json_response=False, allow_redirects=False ) def test_get_download_url_file_version(test_file, test_file_version, mock_box_session): expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}/content' download_url = 'https://dl.boxcloud.com/sdjhfgksdjfgshdbg' - mock_box_session.get.return_value.headers = { - 'location': download_url - } + mock_box_session.get.return_value.headers = {'location': download_url} url = test_file.get_download_url(file_version=test_file_version) mock_box_session.get.assert_called_once_with( expected_url, params={'version': test_file_version.object_id}, expect_json_response=False, - allow_redirects=False + allow_redirects=False, ) assert url == download_url -@pytest.mark.parametrize('params,expected_query,expected_headers', [ - ({}, None, None), - ({'byte_range': (100, 199)}, None, {'Range': 'bytes=100-199'}), - ({'byte_range': (100,)}, None, {'Range': 'bytes=100-'}), -]) -def test_download_to(test_file, mock_box_session, mock_content_response, params, expected_query, expected_headers): +@pytest.mark.parametrize( + 'params,expected_query,expected_headers', + [ + ({}, None, None), + ({'byte_range': (100, 199)}, None, {'Range': 'bytes=100-199'}), + ({'byte_range': (100,)}, None, {'Range': 'bytes=100-'}), + ], +) +def test_download_to( + test_file, + mock_box_session, + mock_content_response, + params, + expected_query, + expected_headers, +): expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}/content' mock_box_session.get.return_value = mock_content_response mock_writeable_stream = BytesIO() @@ -294,11 +306,13 @@ def test_download_to(test_file, mock_box_session, mock_content_response, params, expect_json_response=False, stream=True, params=expected_query, - headers=expected_headers + headers=expected_headers, ) -def test_download_to_file_version(test_file, test_file_version, mock_box_session, mock_content_response): +def test_download_to_file_version( + test_file, test_file_version, mock_box_session, mock_content_response +): expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}/content' mock_box_session.get.return_value = mock_content_response mock_writeable_stream = BytesIO() @@ -310,15 +324,25 @@ def test_download_to_file_version(test_file, test_file_version, mock_box_session expect_json_response=False, stream=True, headers=None, - params={'version': test_file_version.object_id} + params={'version': test_file_version.object_id}, ) -@pytest.mark.parametrize('params,expected_query,expected_headers', [ - ({}, None, None), - ({'byte_range': (100, 199)}, None, {'Range': 'bytes=100-199'}), -]) -def test_get_content(test_file, mock_box_session, mock_content_response, params, expected_query, expected_headers): +@pytest.mark.parametrize( + 'params,expected_query,expected_headers', + [ + ({}, None, None), + ({'byte_range': (100, 199)}, None, {'Range': 'bytes=100-199'}), + ], +) +def test_get_content( + test_file, + mock_box_session, + mock_content_response, + params, + expected_query, + expected_headers, +): expected_url = test_file.get_url('content') mock_box_session.get.return_value = mock_content_response file_content = test_file.content(**params) @@ -327,11 +351,13 @@ def test_get_content(test_file, mock_box_session, mock_content_response, params, expected_url, expect_json_response=False, params=expected_query, - headers=expected_headers + headers=expected_headers, ) -def test_get_content_file_version(test_file, mock_box_session, mock_content_response, test_file_version): +def test_get_content_file_version( + test_file, mock_box_session, mock_content_response, test_file_version +): expected_url = test_file.get_url('content') mock_box_session.get.return_value = mock_content_response file_content = test_file.content(file_version=test_file_version) @@ -340,35 +366,42 @@ def test_get_content_file_version(test_file, mock_box_session, mock_content_resp expected_url, expect_json_response=False, params={'version': test_file_version.object_id}, - headers=None + headers=None, ) -@pytest.mark.parametrize('content_modified_at', [ - '1970-01-01T11:11:11.500+14:00', - datetime(1970, 1, 1, 11, 11, 11, microsecond=500, tzinfo=pytz.timezone('US/Alaska')) -]) +@pytest.mark.parametrize( + 'content_modified_at', + [ + '1970-01-01T11:11:11.500+14:00', + datetime( + 1970, 1, 1, 11, 11, 11, microsecond=500, tzinfo=pytz.timezone('US/Alaska') + ), + ], +) @pytest.mark.parametrize('is_stream', (True, False)) def test_update_contents( - test_file, - mock_box_session, - mock_content_response, - mock_upload_response, - mock_file_path, - etag, - sha1, - upload_using_accelerator, - mock_accelerator_response_for_update, - mock_accelerator_upload_url_for_update, - upload_using_accelerator_fails, - if_match_sha1_header, - is_stream, - content_modified_at + test_file, + mock_box_session, + mock_content_response, + mock_upload_response, + mock_file_path, + etag, + sha1, + upload_using_accelerator, + mock_accelerator_response_for_update, + mock_accelerator_upload_url_for_update, + upload_using_accelerator_fails, + if_match_sha1_header, + is_stream, + content_modified_at, ): # pylint:disable=too-many-locals file_new_name = 'new_file_name' additional_attributes = {'attr': 123} - expected_url = test_file.get_url('content').replace(API.BASE_API_URL, API.UPLOAD_URL) + expected_url = test_file.get_url('content').replace( + API.BASE_API_URL, API.UPLOAD_URL + ) if upload_using_accelerator: if upload_using_accelerator_fails: mock_box_session.options.side_effect = BoxAPIException(400) @@ -429,12 +462,12 @@ def test_update_contents( @pytest.mark.parametrize('is_stream', (True, False)) def test_update_contents_combines_preflight_and_accelerator_calls_if_both_are_requested( - test_file, - mock_box_session, - mock_file_path, - mock_content_response, - mock_accelerator_response_for_update, - is_stream + test_file, + mock_box_session, + mock_file_path, + mock_content_response, + mock_accelerator_response_for_update, + is_stream, ): mock_box_session.options.return_value = mock_accelerator_response_for_update @@ -458,11 +491,11 @@ def test_update_contents_combines_preflight_and_accelerator_calls_if_both_are_re def test_update_contents_with_stream_does_preflight_check_if_specified( - test_file, - preflight_check, - file_size, - preflight_fails, - mock_box_session, + test_file, + preflight_check, + file_size, + preflight_fails, + mock_box_session, ): with patch.object(File, 'preflight_check', return_value=None): kwargs = {'file_stream': BytesIO(b'some bytes')} @@ -488,12 +521,12 @@ def test_update_contents_with_stream_does_preflight_check_if_specified( @patch('boxsdk.object.file.open', mock_open(read_data=b'some bytes'), create=True) def test_update_contents_does_preflight_check_if_specified( - test_file, - mock_file_path, - preflight_check, - file_size, - preflight_fails, - mock_box_session, + test_file, + mock_file_path, + preflight_check, + file_size, + preflight_fails, + mock_box_session, ): with patch.object(File, 'preflight_check', return_value=None): kwargs = {'file_path': mock_file_path} @@ -517,31 +550,34 @@ def test_update_contents_does_preflight_check_if_specified( assert not test_file.preflight_check.called -@pytest.mark.parametrize('params,expected_data', [ - ({}, {'is_download_prevented': False}), - ({'prevent_download': False}, {'is_download_prevented': False}), - ({'prevent_download': True}, {'is_download_prevented': True}), - ({'expire_time': '2018-11-06T19:40:00-08:00'}, - { - 'is_download_prevented': False, - 'expires_at': '2018-11-06T19:40:00-08:00' - }), - ({'expire_time': datetime(2018, 11, 6, 19, 40, 00, tzinfo=pytz.timezone('US/Alaska'))}, - { - 'is_download_prevented': False, - 'expires_at': '2018-11-06T19:40:00+14:00' - }), -]) +@pytest.mark.parametrize( + 'params,expected_data', + [ + ({}, {'is_download_prevented': False}), + ({'prevent_download': False}, {'is_download_prevented': False}), + ({'prevent_download': True}, {'is_download_prevented': True}), + ( + {'expire_time': '2018-11-06T19:40:00-08:00'}, + {'is_download_prevented': False, 'expires_at': '2018-11-06T19:40:00-08:00'}, + ), + ( + { + 'expire_time': datetime( + 2018, 11, 6, 19, 40, 00, tzinfo=pytz.timezone('US/Alaska') + ) + }, + {'is_download_prevented': False, 'expires_at': '2018-11-06T19:40:00+14:00'}, + ), + ], +) def test_lock(test_file, mock_box_session, mock_file_response, params, expected_data): expected_url = test_file.get_url() - expected_body = { - 'lock': { - 'type': 'lock' - } - } + expected_body = {'lock': {'type': 'lock'}} if 'is_download_prevented' in expected_data.keys(): - expected_body['lock']['is_download_prevented'] = expected_data['is_download_prevented'] + expected_body['lock']['is_download_prevented'] = expected_data[ + 'is_download_prevented' + ] if 'expires_at' in expected_data.keys(): expected_body['lock']['expires_at'] = expected_data['expires_at'] @@ -576,24 +612,19 @@ def test_unlock(test_file, mock_box_session, mock_file_response): 'foo.txt', json.dumps({'size': 100, 'name': 'foo.txt'}), ), - # Test case for omitting the name of the file for preflight - ( - 200, - None, - json.dumps({'size': 200}) - ), - ] + (200, None, json.dumps({'size': 200})), + ], ) def test_preflight_check( - test_file, - mock_object_id, - mock_box_session, - mock_accelerator_response_for_update, - mock_accelerator_upload_url_for_update, - size, - name, - expected_data, + test_file, + mock_object_id, + mock_box_session, + mock_accelerator_response_for_update, + mock_accelerator_upload_url_for_update, + size, + name, + expected_data, ): mock_box_session.options.return_value = mock_accelerator_response_for_update kwargs = {'size': size} @@ -611,18 +642,18 @@ def test_preflight_check( def test_get_shared_link( - test_file, - mock_box_session, - shared_link_access, - shared_link_unshared_at, - shared_link_password, - shared_link_can_download, - shared_link_can_preview, - shared_link_can_edit, - shared_link_vanity_name, - test_url, - etag, - if_match_header, + test_file, + mock_box_session, + shared_link_access, + shared_link_unshared_at, + shared_link_password, + shared_link_can_download, + shared_link_can_preview, + shared_link_can_edit, + shared_link_vanity_name, + test_url, + etag, + if_match_header, ): # pylint:disable=redefined-outer-name, protected-access expected_url = test_file.get_url() @@ -637,7 +668,9 @@ def test_get_shared_link( if shared_link_access is not None: expected_data['shared_link']['access'] = shared_link_access if shared_link_unshared_at is not SDK_VALUE_NOT_SET: - expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format(shared_link_unshared_at) + expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format( + shared_link_unshared_at + ) permissions = {} if shared_link_can_download is not None: permissions['can_download'] = shared_link_can_download @@ -672,16 +705,16 @@ def test_get_shared_link( def test_get_shared_link_download_url( - test_file, - mock_box_session, - shared_link_access, - shared_link_unshared_at, - shared_link_password, - shared_link_can_preview, - shared_link_vanity_name, - test_url, - etag, - if_match_header, + test_file, + mock_box_session, + shared_link_access, + shared_link_unshared_at, + shared_link_password, + shared_link_can_preview, + shared_link_vanity_name, + test_url, + etag, + if_match_header, ): # pylint:disable=redefined-outer-name, protected-access expected_url = test_file.get_url() @@ -699,7 +732,9 @@ def test_get_shared_link_download_url( if shared_link_access is not None: expected_data['shared_link']['access'] = shared_link_access if shared_link_unshared_at is not SDK_VALUE_NOT_SET: - expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format(shared_link_unshared_at) + expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format( + shared_link_unshared_at + ) if shared_link_can_preview is not None: expected_data['shared_link']['permissions'] = permissions = {} permissions['can_preview'] = shared_link_can_preview @@ -727,21 +762,17 @@ def test_get_shared_link_download_url( def test_get_comments(test_file, mock_box_session): expected_url = test_file.get_url('comments') - mock_comment1 = { - 'type': 'comment', - 'id': '11111', - 'message': 'Foo' - } + mock_comment1 = {'type': 'comment', 'id': '11111', 'message': 'Foo'} mock_comment2 = { 'type': 'comment', 'id': '22222', - 'tagged_message': 'Well hello there, @[33333:friend]!' + 'tagged_message': 'Well hello there, @[33333:friend]!', } mock_box_session.get.return_value.json.return_value = { 'total_count': 2, 'offset': 0, 'limit': 100, - 'entries': [mock_comment1, mock_comment2] + 'entries': [mock_comment1, mock_comment2], } comments = test_file.get_comments() comment1 = comments.next() @@ -760,18 +791,17 @@ def test_add_comment(test_file, mock_box_session, comment_params): (message_type, message) = comment_params expected_data = { message_type: message, - 'item': { - 'type': 'file', - 'id': test_file.object_id - } + 'item': {'type': 'file', 'id': test_file.object_id}, } mock_box_session.post.return_value.json.return_value = { 'type': 'comment', 'id': comment_id, - message_type: message + message_type: message, } comment = test_file.add_comment(message) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert isinstance(comment, Comment) assert comment.object_id == comment_id @@ -792,7 +822,7 @@ def test_get_previous_versions(test_file, mock_box_session): 'total_count': 2, 'offset': 0, 'limit': 100, - 'entries': [mock_version1, mock_version2] + 'entries': [mock_version1, mock_version2], } versions = test_file.get_previous_versions() version1 = versions.next() @@ -818,29 +848,36 @@ def test_promote_version(test_file, test_file_version, mock_box_session): 'sha1': sha1, } new_version = test_file.promote_version(test_file_version) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(new_version, FileVersion) assert new_version.object_id == '77777' assert new_version.sha1 == sha1 -@pytest.mark.parametrize('params,expected_headers', [ - ({}, None), - ({'etag': 'foobar'}, {'If-Match': 'foobar'}), -]) -def test_delete_version(test_file, test_file_version, mock_box_session, params, expected_headers): +@pytest.mark.parametrize( + 'params,expected_headers', + [ + ({}, None), + ({'etag': 'foobar'}, {'If-Match': 'foobar'}), + ], +) +def test_delete_version( + test_file, test_file_version, mock_box_session, params, expected_headers +): expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}/versions/{test_file_version.object_id}' mock_box_session.delete.return_value.ok = True is_success = test_file.delete_version(test_file_version, **params) - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=expected_headers) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=expected_headers + ) assert is_success is True def test_get_embed_url(test_file, mock_box_session): expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}' - expected_params = { - 'fields': 'expiring_embed_link' - } + expected_params = {'fields': 'expiring_embed_link'} embed_url = 'https://app.box.com/preview/mystuff' mock_box_session.get.return_value.json.return_value = { 'type': 'file', @@ -855,11 +892,16 @@ def test_get_embed_url(test_file, mock_box_session): assert url == embed_url -@pytest.mark.parametrize('rep_hints,expected_headers', [ - (None, None), - ('[pdf]', {'X-Rep-Hints': '[pdf]'}), -]) -def test_get_representation_info(test_file, mock_box_session, rep_hints, expected_headers): +@pytest.mark.parametrize( + 'rep_hints,expected_headers', + [ + (None, None), + ('[pdf]', {'X-Rep-Hints': '[pdf]'}), + ], +) +def test_get_representation_info( + test_file, mock_box_session, rep_hints, expected_headers +): expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}' expected_params = {'fields': 'representations'} @@ -881,7 +923,9 @@ def test_get_representation_info(test_file, mock_box_session, rep_hints, expecte } reps = test_file.get_representation_info(rep_hints=rep_hints) - mock_box_session.get.assert_called_once_with(expected_url, params=expected_params, headers=expected_headers) + mock_box_session.get.assert_called_once_with( + expected_url, params=expected_params, headers=expected_headers + ) assert isinstance(reps, list) assert len(reps) == 1 rep = reps[0] @@ -889,25 +933,37 @@ def test_get_representation_info(test_file, mock_box_session, rep_hints, expecte assert rep['info']['url'] == info_url -@pytest.mark.parametrize('extension,min_width,min_height,max_width,max_height,expected_params', [ - ('png', None, None, None, None, {}), - ('png', None, None, None, None, {}), - ('jpg', None, None, None, None, {}), - ('png', 1, 2, None, None, {'min_width': 1, 'min_height': 2}), - ('png', 1, 2, 3, 4, {'min_width': 1, 'min_height': 2, 'max_width': 3, 'max_height': 4}), -]) +@pytest.mark.parametrize( + 'extension,min_width,min_height,max_width,max_height,expected_params', + [ + ('png', None, None, None, None, {}), + ('png', None, None, None, None, {}), + ('jpg', None, None, None, None, {}), + ('png', 1, 2, None, None, {'min_width': 1, 'min_height': 2}), + ( + 'png', + 1, + 2, + 3, + 4, + {'min_width': 1, 'min_height': 2, 'max_width': 3, 'max_height': 4}, + ), + ], +) def test_get_thumbnail( - test_file, - mock_box_session, - mock_content_response, - extension, - min_width, - min_height, - max_width, - max_height, - expected_params, + test_file, + mock_box_session, + mock_content_response, + extension, + min_width, + min_height, + max_width, + max_height, + expected_params, ): - expected_url = f'{API.BASE_API_URL}/files/{test_file.object_id}/thumbnail.{extension}' + expected_url = ( + f'{API.BASE_API_URL}/files/{test_file.object_id}/thumbnail.{extension}' + ) mock_box_session.get.return_value = mock_content_response thumb = test_file.get_thumbnail( @@ -918,20 +974,25 @@ def test_get_thumbnail( max_height=max_height, ) - mock_box_session.get.assert_called_once_with(expected_url, expect_json_response=False, params=expected_params) + mock_box_session.get.assert_called_once_with( + expected_url, expect_json_response=False, params=expected_params + ) assert thumb == mock_content_response.content -@pytest.mark.parametrize('dimensions,extension', [ - ('92x92', 'png'), - ('92x92', 'jpg'), -]) +@pytest.mark.parametrize( + 'dimensions,extension', + [ + ('92x92', 'png'), + ('92x92', 'jpg'), + ], +) def test_get_thumbnail_representation( - test_file, - mock_box_session, - mock_content_response, - dimensions, - extension, + test_file, + mock_box_session, + mock_content_response, + dimensions, + extension, ): representation_url = f'{API.BASE_API_URL}/files/{test_file.object_id}' content_url = 'https://dl.boxcloud.com/api/2.0/internal_files/123/versions/345/representations/jpg/content/' @@ -943,40 +1004,44 @@ def test_get_thumbnail_representation( 'representations': { 'entries': [ { - 'content': { - 'url_template': content_url + '{+asset_path}' - }, + 'content': {'url_template': content_url + '{+asset_path}'}, 'info': { - 'url': 'https://api.box.com/2.0/internal_files/123/versions/345/representations/jpg' + 'url': ( + 'https://api.box.com/2.0/internal_files/123/versions/345/representations/jpg' + ) }, 'properties': {}, 'representation': 'pdf', - 'status': { - 'state': 'success' - } + 'status': {'state': 'success'}, } ] }, - 'type': 'file' + 'type': 'file', } - mock_box_session.get.side_effect = [mock_representations_response, mock_content_response] + mock_box_session.get.side_effect = [ + mock_representations_response, + mock_content_response, + ] thumb = test_file.get_thumbnail_representation( dimensions=dimensions, extension=extension, ) - mock_box_session.get.assert_any_call(representation_url, headers={'X-Rep-Hints': f'[{extension}?dimensions=92x92]'}, - params={'fields': 'representations'}) + mock_box_session.get.assert_any_call( + representation_url, + headers={'X-Rep-Hints': f'[{extension}?dimensions=92x92]'}, + params={'fields': 'representations'}, + ) mock_box_session.get.assert_any_call(content_url, expect_json_response=False) assert thumb == mock_content_response.content def test_get_thumbnail_representation_not_found( - test_file, - mock_box_session, - mock_content_response, + test_file, + mock_box_session, + mock_content_response, ): representation_url = f'{API.BASE_API_URL}/files/{test_file.object_id}' dimensions = '100x100' @@ -989,10 +1054,13 @@ def test_get_thumbnail_representation_not_found( 'representations': { 'entries': [], }, - 'type': 'file' + 'type': 'file', } - mock_box_session.get.side_effect = [mock_representations_response, mock_content_response] + mock_box_session.get.side_effect = [ + mock_representations_response, + mock_content_response, + ] thumb = test_file.get_thumbnail_representation( dimensions=dimensions, @@ -1008,9 +1076,9 @@ def test_get_thumbnail_representation_not_found( def test_get_thumbnail_representation_not_available( - test_file, - mock_box_session, - mock_content_response, + test_file, + mock_box_session, + mock_content_response, ): representation_url = f'{API.BASE_API_URL}/files/{test_file.object_id}' dimensions = '100x100' @@ -1023,22 +1091,25 @@ def test_get_thumbnail_representation_not_available( 'representations': { 'entries': [ { - 'content': { - 'url_template': 'content_url {+asset_path}' - }, + 'content': {'url_template': 'content_url {+asset_path}'}, 'info': { - 'url': 'https://api.box.com/2.0/internal_files/123/versions/345/representations/jpg' + 'url': ( + 'https://api.box.com/2.0/internal_files/123/versions/345/representations/jpg' + ) }, 'properties': {}, 'representation': 'pdf', - 'status': {'state': 'error', 'code': 'error_password_protected'} + 'status': {'state': 'error', 'code': 'error_password_protected'}, } ] }, - 'type': 'file' + 'type': 'file', } - mock_box_session.get.side_effect = [mock_representations_response, mock_content_response] + mock_box_session.get.side_effect = [ + mock_representations_response, + mock_content_response, + ] thumb = test_file.get_thumbnail_representation( dimensions=dimensions, @@ -1060,13 +1131,13 @@ def test_get_thumbnail_representation_not_available( "2035-03-04T10:14:24.000+14:00", "2035/03/04 10:14:24.000+14:00", lazy_fixture('mock_timezone_aware_datetime_obj'), - ) + ), ) def test_set_diposition_at( - test_file, - mock_box_session, - disposition_at, - mock_datetime_rfc3339_str, + test_file, + mock_box_session, + disposition_at, + mock_datetime_rfc3339_str, ): expected_url = test_file.get_url() expected_data = {'disposition_at': mock_datetime_rfc3339_str} diff --git a/test/unit/object/test_file_request.py b/test/unit/object/test_file_request.py index a79c81814..a4723dacb 100644 --- a/test/unit/object/test_file_request.py +++ b/test/unit/object/test_file_request.py @@ -14,10 +14,12 @@ def test_get(test_file_request, mock_box_session): mock_box_session.get.return_value.json.return_value = { 'type': test_file_request.object_type, 'id': test_file_request.object_id, - 'title': 'File Request' + 'title': 'File Request', } file_request = test_file_request.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(file_request, FileRequest) assert file_request['type'] == file_request.object_type assert file_request['id'] == file_request.object_id @@ -39,7 +41,9 @@ def test_update(test_file_request, mock_box_session): 'status': StatusState.INACTIVE, } file_request = test_file_request.update_info(data=data) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(file_request, FileRequest) assert file_request['type'] == test_file_request.object_type assert file_request['id'] == test_file_request.object_id @@ -47,13 +51,18 @@ def test_update(test_file_request, mock_box_session): assert file_request['status'] == StatusState.INACTIVE -@pytest.mark.parametrize('expires_at', [ - '2019-07-01T22:02:24+14:00', - datetime(2019, 7, 1, 22, 2, 24, tzinfo=pytz.timezone('US/Alaska')) -]) +@pytest.mark.parametrize( + 'expires_at', + [ + '2019-07-01T22:02:24+14:00', + datetime(2019, 7, 1, 22, 2, 24, tzinfo=pytz.timezone('US/Alaska')), + ], +) def test_copy(test_file_request, mock_box_session, expires_at): new_folder_id = '100' - expected_url = f'{API.BASE_API_URL}/file_requests/{test_file_request.object_id}/copy' + expected_url = ( + f'{API.BASE_API_URL}/file_requests/{test_file_request.object_id}/copy' + ) expected_expires_at = '2019-07-01T22:02:24+14:00' mock_box_session.post.return_value.json.return_value = { 'type': test_file_request.object_type, @@ -67,7 +76,9 @@ def test_copy(test_file_request, mock_box_session, expires_at): } new_title = 'File Request Copied' new_folder = Folder(mock_box_session, object_id=new_folder_id) - file_request = test_file_request.copy(title=new_title, folder=new_folder, expires_at=expires_at) + file_request = test_file_request.copy( + title=new_title, folder=new_folder, expires_at=expires_at + ) data = { 'folder': { 'id': new_folder_id, @@ -87,4 +98,6 @@ def test_copy(test_file_request, mock_box_session, expires_at): def test_delete(test_file_request, mock_box_session): expected_url = f'{API.BASE_API_URL}/file_requests/{test_file_request.object_id}' test_file_request.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={}) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=None, params={} + ) diff --git a/test/unit/object/test_file_version_retention.py b/test/unit/object/test_file_version_retention.py index 8e24b76ea..73119b5d4 100644 --- a/test/unit/object/test_file_version_retention.py +++ b/test/unit/object/test_file_version_retention.py @@ -9,5 +9,7 @@ def test_get(test_file_version_retention, mock_box_session): 'id': test_file_version_retention.object_id, } file_version_retention = test_file_version_retention.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(file_version_retention, FileVersionRetention) diff --git a/test/unit/object/test_folder.py b/test/unit/object/test_folder.py index f5a97adb8..0f8efa4cf 100644 --- a/test/unit/object/test_folder.py +++ b/test/unit/object/test_folder.py @@ -19,7 +19,6 @@ from boxsdk.session.box_response import BoxResponse from boxsdk.util.chunked_uploader import ChunkedUploader - # pylint:disable=protected-access # pylint:disable=redefined-outer-name from boxsdk.util.datetime_formatter import normalize_date_to_rfc3339_format @@ -32,7 +31,9 @@ def mock_new_upload_accelerator_url(): @pytest.fixture(scope='function') -def mock_accelerator_response_for_new_uploads(make_mock_box_request, mock_new_upload_accelerator_url): +def mock_accelerator_response_for_new_uploads( + make_mock_box_request, mock_new_upload_accelerator_url +): mock_response, _ = make_mock_box_request( response={ 'upload_url': mock_new_upload_accelerator_url, @@ -60,7 +61,7 @@ def mock_items_response(mock_items): # pylint:disable=redefined-outer-name def get_response(limit, offset): items_json, items = mock_items - entries = items_json[offset:limit + offset] + entries = items_json[offset : limit + offset] mock_box_response = Mock(BoxResponse) mock_network_response = Mock(DefaultNetworkResponse) mock_box_response.network_response = mock_network_response @@ -73,13 +74,18 @@ def get_response(limit, offset): mock_box_response.content = json.dumps(mock_json).encode() mock_box_response.status_code = 200 mock_box_response.ok = True - return mock_box_response, items[offset:limit + offset] + return mock_box_response, items[offset : limit + offset] + return get_response @pytest.mark.parametrize('use_upload_session_urls', [True, False]) def test_get_chunked_uploader( - mock_box_session, mock_content_response, mock_file_path, test_folder, use_upload_session_urls + mock_box_session, + mock_content_response, + mock_file_path, + test_folder, + use_upload_session_urls, ): expected_url = f'{API.UPLOAD_URL}/files/upload_sessions' mock_file_stream = BytesIO(mock_content_response.content) @@ -108,7 +114,9 @@ def test_get_chunked_uploader( chunked_uploader = test_folder.get_chunked_uploader( mock_file_path, use_upload_session_urls=use_upload_session_urls ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) upload_session = chunked_uploader._upload_session assert upload_session.part_size == part_size assert upload_session.total_parts == total_parts @@ -144,7 +152,9 @@ def test_create_upload_session(test_folder, mock_box_session, use_upload_session upload_session = test_folder.create_upload_session( file_size, file_name, use_upload_session_urls=use_upload_session_urls ) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert isinstance(upload_session, UploadSession) assert upload_session.part_size == part_size assert upload_session.total_parts == total_parts @@ -159,7 +169,7 @@ def mock_items_response_with_marker(mock_items): # pylint:disable=redefined-outer-name def get_response(limit, offset): items_json, items = mock_items - entries = items_json[offset:limit + offset] + entries = items_json[offset : limit + offset] mock_box_response = Mock(BoxResponse) mock_network_response = Mock(DefaultNetworkResponse) mock_box_response.network_response = mock_network_response @@ -172,24 +182,48 @@ def get_response(limit, offset): mock_box_response.content = json.dumps(mock_json).encode() mock_box_response.status_code = 200 mock_box_response.ok = True - return mock_box_response, items[offset:limit + offset] + return mock_box_response, items[offset : limit + offset] + return get_response -def _assert_collaborator_added(test_folder, collaborator, mock_box_session, mock_collab_response, notify, role, can_view_path, data): +def _assert_collaborator_added( + test_folder, + collaborator, + mock_box_session, + mock_collab_response, + notify, + role, + can_view_path, + data, +): mock_box_session.post.return_value = mock_collab_response - collaboration = test_folder.add_collaborator(collaborator, role, notify, can_view_path) + collaboration = test_folder.add_collaborator( + collaborator, role, notify, can_view_path + ) assert isinstance(collaboration, Collaboration) expected_url = API.BASE_API_URL + '/collaborations' params = {'notify': notify} - mock_box_session.post.assert_called_once_with(expected_url, expect_json_response=True, data=data, params=params) + mock_box_session.post.assert_called_once_with( + expected_url, expect_json_response=True, data=data, params=params + ) @pytest.mark.parametrize('accessible_by', ['user', 'group', 'email']) @pytest.mark.parametrize('notify', [True, False]) @pytest.mark.parametrize('role', iter(CollaborationRole)) @pytest.mark.parametrize('can_view_path', [True, False]) -def test_add_collaborator(test_folder, mock_user, mock_group, mock_box_session, mock_collab_response, accessible_by, notify, role, can_view_path): +def test_add_collaborator( + test_folder, + mock_user, + mock_group, + mock_box_session, + mock_collab_response, + accessible_by, + notify, + role, + can_view_path, +): accessible_dict = { 'user': (mock_user, {'id': mock_user.object_id, 'type': 'user'}), 'group': (mock_group, {'id': mock_group.object_id, 'type': 'group'}), @@ -206,7 +240,16 @@ def test_add_collaborator(test_folder, mock_user, mock_group, mock_box_session, if can_view_path: body_params['can_view_path'] = True data = json.dumps(body_params) - _assert_collaborator_added(test_folder, invitee, mock_box_session, mock_collab_response, notify, role, can_view_path, data) + _assert_collaborator_added( + test_folder, + invitee, + mock_box_session, + mock_collab_response, + notify, + role, + can_view_path, + data, + ) def test_add_collaborator_raises_for_bad_type(test_folder): @@ -226,18 +269,34 @@ def test_delete_folder(test_folder, mock_box_session, recursive, etag, if_match_ ) -@pytest.mark.parametrize('limit,offset,fields,sort,direction', [ - (1, 0, None, None, None), - (100, 0, ['foo', 'bar'], None, None), - (1, 1, None, None, None), - (1, 0, None, 'name', 'ASC'), - (1, 1, None, 'date', 'DESC') -]) -def test_get_items(test_folder, mock_box_session, mock_items_response, limit, offset, fields, sort, direction): +@pytest.mark.parametrize( + 'limit,offset,fields,sort,direction', + [ + (1, 0, None, None, None), + (100, 0, ['foo', 'bar'], None, None), + (1, 1, None, None, None), + (1, 0, None, 'name', 'ASC'), + (1, 1, None, 'date', 'DESC'), + ], +) +def test_get_items( + test_folder, + mock_box_session, + mock_items_response, + limit, + offset, + fields, + sort, + direction, +): # pylint:disable=redefined-outer-name expected_url = test_folder.get_url('items') - mock_box_session.get.return_value, expected_items = mock_items_response(limit, offset) - items = test_folder.get_items(limit, offset, fields=fields, sort=sort, direction=direction) + mock_box_session.get.return_value, expected_items = mock_items_response( + limit, offset + ) + items = test_folder.get_items( + limit, offset, fields=fields, sort=sort, direction=direction + ) expected_params = {'limit': limit, 'offset': offset} if fields: expected_params['fields'] = ','.join(fields) @@ -251,34 +310,36 @@ def test_get_items(test_folder, mock_box_session, mock_items_response, limit, of assert all(i.id == e.object_id for (i, e) in zip(items, expected_items)) -@pytest.mark.parametrize('content_created_at', [ - '1970-01-01T00:00:00+00:00', - datetime(1970, 1, 1, 0, 0, 0, tzinfo=pytz.UTC), - None -]) -@pytest.mark.parametrize('content_modified_at', [ - '1970-01-01T11:11:11+00:00', - datetime(1970, 1, 1, 11, 11, 11, tzinfo=pytz.UTC), - None -]) +@pytest.mark.parametrize( + 'content_created_at', + ['1970-01-01T00:00:00+00:00', datetime(1970, 1, 1, 0, 0, 0, tzinfo=pytz.UTC), None], +) +@pytest.mark.parametrize( + 'content_modified_at', + [ + '1970-01-01T11:11:11+00:00', + datetime(1970, 1, 1, 11, 11, 11, tzinfo=pytz.UTC), + None, + ], +) @pytest.mark.parametrize('is_stream', (True, False)) def test_upload( - test_folder, - mock_box_session, - mock_content_response, - mock_upload_response, - mock_file_path, - mock_object_id, - upload_using_accelerator, - mock_accelerator_response_for_new_uploads, - mock_new_upload_accelerator_url, - upload_using_accelerator_fails, - is_stream, - etag, - sha1, - if_match_sha1_header, - content_created_at, - content_modified_at + test_folder, + mock_box_session, + mock_content_response, + mock_upload_response, + mock_file_path, + mock_object_id, + upload_using_accelerator, + mock_accelerator_response_for_new_uploads, + mock_new_upload_accelerator_url, + upload_using_accelerator_fails, + is_stream, + etag, + sha1, + if_match_sha1_header, + content_created_at, + content_modified_at, ): # pylint:disable=too-many-locals # pylint:disable=too-many-arguments @@ -289,7 +350,9 @@ def test_upload( if upload_using_accelerator_fails: mock_box_session.options.side_effect = BoxAPIException(400) else: - mock_box_session.options.return_value = mock_accelerator_response_for_new_uploads + mock_box_session.options.return_value = ( + mock_accelerator_response_for_new_uploads + ) expected_url = mock_new_upload_accelerator_url mock_box_session.post.return_value = mock_upload_response @@ -340,7 +403,7 @@ def test_upload( files=mock_files, data=data, headers=if_match_sha1_header, - stream_file_content=True + stream_file_content=True, ) assert isinstance(new_file, File) assert new_file.object_id == mock_object_id @@ -353,12 +416,12 @@ def test_upload( @pytest.mark.parametrize('is_stream', (True, False)) def test_upload_combines_preflight_and_accelerator_calls_if_both_are_requested( - test_folder, - mock_box_session, - mock_file_path, - mock_content_response, - mock_accelerator_response_for_new_uploads, - is_stream + test_folder, + mock_box_session, + mock_file_path, + mock_content_response, + mock_accelerator_response_for_new_uploads, + is_stream, ): mock_box_session.options.return_value = mock_accelerator_response_for_new_uploads @@ -383,11 +446,11 @@ def test_upload_combines_preflight_and_accelerator_calls_if_both_are_requested( def test_upload_stream_does_preflight_check_if_specified( - mock_box_session, - test_folder, - preflight_check, - preflight_fails, - file_size, + mock_box_session, + test_folder, + preflight_check, + preflight_fails, + file_size, ): with patch.object(Folder, 'preflight_check', return_value=None): kwargs = {'file_stream': BytesIO(b'some bytes'), 'file_name': 'foo.txt'} @@ -403,7 +466,9 @@ def test_upload_stream_does_preflight_check_if_specified( test_folder.upload_stream(**kwargs) if preflight_check: - test_folder.preflight_check.assert_called_once_with(size=file_size, name='foo.txt') + test_folder.preflight_check.assert_called_once_with( + size=file_size, name='foo.txt' + ) _assert_post_called_correctly(mock_box_session, preflight_fails) else: assert not test_folder.preflight_check.called @@ -418,12 +483,12 @@ def _assert_post_called_correctly(mock_box_session, preflight_fails): @patch('boxsdk.object.folder.open', mock_open(read_data=b'some bytes'), create=True) def test_upload_does_preflight_check_if_specified( - mock_box_session, - test_folder, - mock_file_path, - preflight_check, - preflight_fails, - file_size, + mock_box_session, + test_folder, + mock_file_path, + preflight_check, + preflight_fails, + file_size, ): with patch.object(Folder, 'preflight_check', return_value=None): kwargs = {'file_path': mock_file_path, 'file_name': 'foo.txt'} @@ -439,7 +504,9 @@ def test_upload_does_preflight_check_if_specified( test_folder.upload(**kwargs) if preflight_check: - test_folder.preflight_check.assert_called_once_with(size=file_size, name='foo.txt') + test_folder.preflight_check.assert_called_once_with( + size=file_size, name='foo.txt' + ) _assert_post_called_correctly(mock_box_session, preflight_fails) else: assert not test_folder.preflight_check.called @@ -448,13 +515,15 @@ def test_upload_does_preflight_check_if_specified( @patch('boxsdk.object.folder.open', mock_open(read_data=b'some bytes'), create=True) @pytest.mark.parametrize('stream_file_content', (True, False)) def test_upload_if_flag_stream_file_content_is_passed_to_session( - mock_box_session, - test_folder, - stream_file_content, + mock_box_session, + test_folder, + stream_file_content, ): expected_url = f'{API.UPLOAD_URL}/files/content' - test_folder.upload('foo.txt', file_name='foo.txt', stream_file_content=stream_file_content) + test_folder.upload( + 'foo.txt', file_name='foo.txt', stream_file_content=stream_file_content + ) mock_files = {'file': ('unused', ANY)} mock_box_session.post.assert_called_once_with( @@ -463,10 +532,13 @@ def test_upload_if_flag_stream_file_content_is_passed_to_session( files=mock_files, expect_json_response=False, headers=None, - stream_file_content=stream_file_content) + stream_file_content=stream_file_content, + ) -def test_create_subfolder(test_folder, mock_box_session, mock_object_id, mock_folder_response): +def test_create_subfolder( + test_folder, mock_box_session, mock_object_id, mock_folder_response +): expected_url = test_folder.get_type_url() mock_box_session.post.return_value = mock_folder_response new_folder = test_folder.create_subfolder('name') @@ -477,17 +549,17 @@ def test_create_subfolder(test_folder, mock_box_session, mock_object_id, mock_fo def test_get_shared_link( - test_folder, - mock_box_session, - shared_link_access, - shared_link_unshared_at, - shared_link_password, - shared_link_can_download, - shared_link_can_preview, - shared_link_vanity_name, - test_url, - etag, - if_match_header, + test_folder, + mock_box_session, + shared_link_access, + shared_link_unshared_at, + shared_link_password, + shared_link_can_download, + shared_link_can_preview, + shared_link_vanity_name, + test_url, + etag, + if_match_header, ): # pylint:disable=redefined-outer-name, protected-access expected_url = test_folder.get_url() @@ -502,7 +574,9 @@ def test_get_shared_link( if shared_link_access is not None: expected_data['shared_link']['access'] = shared_link_access if shared_link_unshared_at is not SDK_VALUE_NOT_SET: - expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format(shared_link_unshared_at) + expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format( + shared_link_unshared_at + ) if shared_link_can_download is not None or shared_link_can_preview is not None: expected_data['shared_link']['permissions'] = permissions = {} if shared_link_can_download is not None: @@ -533,27 +607,33 @@ def test_get_shared_link( @pytest.mark.parametrize('sync_state', iter(FolderSyncState)) -def test_update_sync_state(test_folder, mock_folder_response, mock_box_session, sync_state): +def test_update_sync_state( + test_folder, mock_folder_response, mock_box_session, sync_state +): expected_url = test_folder.get_url() mock_box_session.put.return_value = mock_folder_response data = {'sync_state': sync_state} update_response = test_folder.update_sync_state(sync_state) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), params=None, headers=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), params=None, headers=None + ) assert isinstance(update_response, Folder) assert update_response.object_id == test_folder.object_id def test_preflight( - test_folder, - mock_object_id, - mock_box_session, - mock_accelerator_response_for_new_uploads, - mock_new_upload_accelerator_url, + test_folder, + mock_object_id, + mock_box_session, + mock_accelerator_response_for_new_uploads, + mock_new_upload_accelerator_url, ): new_file_size, new_file_name = 100, 'foo.txt' mock_box_session.options.return_value = mock_accelerator_response_for_new_uploads - accelerator_url = test_folder.preflight_check(size=new_file_size, name=new_file_name) + accelerator_url = test_folder.preflight_check( + size=new_file_size, name=new_file_name + ) mock_box_session.options.assert_called_once_with( url=f'{API.BASE_API_URL}/files/content', @@ -569,7 +649,9 @@ def test_preflight( assert accelerator_url == mock_new_upload_accelerator_url -def test_create_web_link_returns_the_correct_web_link_object(test_folder, mock_box_session): +def test_create_web_link_returns_the_correct_web_link_object( + test_folder, mock_box_session +): expected_url = f"{API.BASE_API_URL}/web_links" expected_name = 'Test WebLink' description = 'Test Description' @@ -579,9 +661,11 @@ def test_create_web_link_returns_the_correct_web_link_object(test_folder, mock_b 'id': '42', 'url': test_web_link_url, 'name': expected_name, - 'description': description + 'description': description, } - new_web_link = test_folder.create_web_link(test_web_link_url, expected_name, description) + new_web_link = test_folder.create_web_link( + test_web_link_url, expected_name, description + ) data = { 'url': test_web_link_url, 'parent': { @@ -657,7 +741,9 @@ def test_cascade_metadata(test_folder, mock_box_session, test_metadata_template) cascade_policy = test_folder.cascade_metadata(test_metadata_template) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(cascade_policy, MetadataCascadePolicy) assert cascade_policy.object_id == '84113349-794d-445c-b93c-d8481b223434' enterprise = cascade_policy.owner_enterprise @@ -681,24 +767,18 @@ def test_get_folder_locks(test_folder, mock_box_session): "etag": "1", "type": "folder", "sequence_id": "3", - "name": "Contracts" + "name": "Contracts", }, "id": "12345678", "type": "folder_lock", - "created_by": { - "id": "11446498", - "type": "user" - }, + "created_by": {"id": "11446498", "type": "user"}, "created_at": "2020-09-14T23:12:53Z", - "locked_operations": { - "move": True, - "delete": True - }, - "lock_type": "freeze" + "locked_operations": {"move": True, "delete": True}, + "lock_type": "freeze", } ], "limit": 1000, - "next_marker": None + "next_marker": None, } folder_locks = test_folder.get_locks() @@ -715,40 +795,30 @@ def test_get_folder_locks(test_folder, mock_box_session): def test_create_folder_lock(test_folder, mock_box_session): expected_url = f'{API.BASE_API_URL}/folder_locks' expected_body = { - "folder": { - "type": "folder", - "id": test_folder.object_id - }, - "locked_operations": { - "move": True, - "delete": True - } + "folder": {"type": "folder", "id": test_folder.object_id}, + "locked_operations": {"move": True, "delete": True}, } mock_box_session.post.return_value.json.return_value = { "id": "12345678", "type": "folder_lock", "created_at": "2020-09-14T23:12:53Z", - "created_by": { - "id": "11446498", - "type": "user" - }, + "created_by": {"id": "11446498", "type": "user"}, "folder": { "id": "12345", "type": "folder", "etag": "1", "name": "Contracts", - "sequence_id": "3" + "sequence_id": "3", }, "lock_type": "freeze", - "locked_operations": { - "delete": True, - "move": True - } + "locked_operations": {"delete": True, "move": True}, } lock = test_folder.create_lock() - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert lock.id == '12345678' assert lock.folder.id == '12345' assert lock.locked_operations['move'] @@ -760,8 +830,5 @@ def test_delete_folder_lock(test_folder_lock, mock_box_session): expected_url = f'{API.BASE_API_URL}/folder_locks/{test_folder_lock.object_id}' test_folder_lock.delete() mock_box_session.delete.assert_called_once_with( - expected_url, - expect_json_response=False, - headers=None, - params={} + expected_url, expect_json_response=False, headers=None, params={} ) diff --git a/test/unit/object/test_group.py b/test/unit/object/test_group.py index f525d878c..0cb213a7a 100644 --- a/test/unit/object/test_group.py +++ b/test/unit/object/test_group.py @@ -22,9 +22,9 @@ def delete_group_response(): def test_delete_group_return_the_correct_response( - mock_group, - mock_box_session, - delete_group_response, + mock_group, + mock_box_session, + delete_group_response, ): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value = delete_group_response @@ -33,49 +33,67 @@ def test_delete_group_return_the_correct_response( # pylint:disable=protected-access expected_url = mock_group.get_url() # pylint:enable=protected-access - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True @pytest.mark.parametrize('role', ['member', 'admin']) -def test_add_member(test_group, mock_box_session, mock_add_member_response, mock_user, role): +def test_add_member( + test_group, mock_box_session, mock_add_member_response, mock_user, role +): expected_url = f'{API.BASE_API_URL}/group_memberships' mock_box_session.post.return_value = mock_add_member_response - new_group_membership = test_group.add_member(mock_user, role, configurable_permissions={'can_run_reports': True}) - data = json.dumps({ - 'user': {'id': mock_user.object_id}, - 'group': {'id': test_group.object_id}, - 'role': role, - 'configurable_permissions': {'can_run_reports': True} - }) + new_group_membership = test_group.add_member( + mock_user, role, configurable_permissions={'can_run_reports': True} + ) + data = json.dumps( + { + 'user': {'id': mock_user.object_id}, + 'group': {'id': test_group.object_id}, + 'role': role, + 'configurable_permissions': {'can_run_reports': True}, + } + ) mock_box_session.post.assert_called_once_with(expected_url, data=data) assert isinstance(new_group_membership, GroupMembership) -def test_add_member_default_permission(test_group, mock_box_session, mock_add_member_response, mock_user): +def test_add_member_default_permission( + test_group, mock_box_session, mock_add_member_response, mock_user +): expected_url = f'{API.BASE_API_URL}/group_memberships' mock_box_session.post.return_value = mock_add_member_response new_group_membership = test_group.add_member(mock_user, 'member') - data = json.dumps({ - 'user': {'id': mock_user.object_id}, - 'group': {'id': test_group.object_id}, - 'role': 'member', - }) + data = json.dumps( + { + 'user': {'id': mock_user.object_id}, + 'group': {'id': test_group.object_id}, + 'role': 'member', + } + ) mock_box_session.post.assert_called_once_with(expected_url, data=data) assert isinstance(new_group_membership, GroupMembership) -def test_add_member_none_permission(test_group, mock_box_session, mock_add_member_response, mock_user): +def test_add_member_none_permission( + test_group, mock_box_session, mock_add_member_response, mock_user +): expected_url = f'{API.BASE_API_URL}/group_memberships' mock_box_session.post.return_value = mock_add_member_response - new_group_membership = test_group.add_member(mock_user, 'member', configurable_permissions=None) - data = json.dumps({ - 'user': {'id': mock_user.object_id}, - 'group': {'id': test_group.object_id}, - 'role': 'member', - 'configurable_permissions': None - }) + new_group_membership = test_group.add_member( + mock_user, 'member', configurable_permissions=None + ) + data = json.dumps( + { + 'user': {'id': mock_user.object_id}, + 'group': {'id': test_group.object_id}, + 'role': 'member', + 'configurable_permissions': None, + } + ) mock_box_session.post.assert_called_once_with(expected_url, data=data) assert isinstance(new_group_membership, GroupMembership) @@ -101,6 +119,7 @@ def mock_membership_responses(mock_membership_dict_stream): The generator generates a sequence of 'group membership' mock_box_responses each containing page_size items, until 'total' entries have been returned """ + # pylint:disable=redefined-outer-name def number_entries_per_response(total, page_size, hidden_in_batch): if not hidden_in_batch: @@ -114,7 +133,9 @@ def take(iterable, number): def membership_responses(total, page_size, hidden_in_batch=None): offset = 0 - for number_entries in number_entries_per_response(total, page_size, hidden_in_batch): + for number_entries in number_entries_per_response( + total, page_size, hidden_in_batch + ): entries = take(mock_membership_dict_stream, number_entries) mock_box_response = Mock(BoxResponse) @@ -134,14 +155,19 @@ def membership_responses(total, page_size, hidden_in_batch=None): return membership_responses -@pytest.mark.parametrize('total, page_size', [ - (0, 6), - (5, 6), - (6, 6), - (5, 4), - (9, 4), -]) -def test_get_memberships(test_group, mock_box_session, mock_membership_responses, total, page_size): +@pytest.mark.parametrize( + 'total, page_size', + [ + (0, 6), + (5, 6), + (6, 6), + (5, 4), + (9, 4), + ], +) +def test_get_memberships( + test_group, mock_box_session, mock_membership_responses, total, page_size +): # pylint:disable=redefined-outer-name # Each call the 'get' (the GET next page call) will return the next response mock_box_session.get.side_effect = mock_membership_responses(total, page_size) @@ -157,13 +183,18 @@ def test_get_memberships(test_group, mock_box_session, mock_membership_responses assert count == total -@pytest.mark.parametrize('hidden_in_batch', [ - (1, 0, 0), - (0, 0, 1), - (10, 10, 9), - (10, 10, 10), -]) -def test_get_memberships_with_hidden_results(test_group, mock_box_session, mock_membership_responses, hidden_in_batch): +@pytest.mark.parametrize( + 'hidden_in_batch', + [ + (1, 0, 0), + (0, 0, 1), + (10, 10, 9), + (10, 10, 10), + ], +) +def test_get_memberships_with_hidden_results( + test_group, mock_box_session, mock_membership_responses, hidden_in_batch +): """ This test verifies that the SDK properly deals with missing (aka hidden) data potentially present in a paged API. The API might indicate that the total_size is X, but in actuality the pages API could return less than X @@ -176,7 +207,9 @@ def test_get_memberships_with_hidden_results(test_group, mock_box_session, mock_ total_hidden = sum(hidden_in_batch) # Each call the 'get' (the GET next page call) will return the next response - mock_box_session.get.side_effect = mock_membership_responses(total, page_size, hidden_in_batch=hidden_in_batch) + mock_box_session.get.side_effect = mock_membership_responses( + total, page_size, hidden_in_batch=hidden_in_batch + ) # Get all the members all_members = test_group.get_memberships(limit=page_size, offset=0) @@ -194,40 +227,33 @@ def test_get_group_collaborations(test_group, mock_box_session): mock_collaboration = { 'type': 'collaboration', 'id': '12345', - 'created_by': { - 'type': 'user', - 'id': '33333' - } + 'created_by': {'type': 'user', 'id': '33333'}, } mock_box_session.get.return_value.json.return_value = { 'limit': 100, 'entries': [mock_collaboration], 'offset': 0, - 'total_count': 1 + 'total_count': 1, } collaborations = test_group.get_collaborations(fields=['type', 'id', 'created_by']) collaboration = collaborations.next() - mock_box_session.get.assert_called_once_with(expected_url, params={'offset': None, 'fields': 'type,id,created_by'}) + mock_box_session.get.assert_called_once_with( + expected_url, params={'offset': None, 'fields': 'type,id,created_by'} + ) assert isinstance(collaboration, Collaboration) assert collaboration.id == mock_collaboration['id'] assert collaboration.created_by['id'] == mock_collaboration['created_by']['id'] -def test_base_api_json_object_returns_correctly(test_group_membership, mock_box_session): +def test_base_api_json_object_returns_correctly( + test_group_membership, mock_box_session +): expected_data = { 'type': 'group_membership', 'id': '12345', 'test': [ - { - 'user': { - 'type': 'user' - } - }, - { - 'group': { - 'type': 'group' - } - }, + {'user': {'type': 'user'}}, + {'group': {'type': 'group'}}, ], 'user': { 'type': 'user', @@ -235,11 +261,7 @@ def test_base_api_json_object_returns_correctly(test_group_membership, mock_box_ 'name': 'Test User', 'login': 'test@example.com', }, - 'group': { - 'type': 'group', - 'id': '54321', - 'name': 'Test' - }, + 'group': {'type': 'group', 'id': '54321', 'name': 'Test'}, 'role': 'admin', 'configurable_permissions': { 'can_run_reports': False, diff --git a/test/unit/object/test_item.py b/test/unit/object/test_item.py index a540f7396..d8e96a6d9 100644 --- a/test/unit/object/test_item.py +++ b/test/unit/object/test_item.py @@ -9,7 +9,9 @@ @pytest.fixture(params=('file', 'folder')) -def test_item_and_response(test_file, test_folder, mock_file_response, mock_folder_response, request): +def test_item_and_response( + test_file, test_folder, mock_file_response, mock_folder_response, request +): if request.param == 'file': return test_file, mock_file_response return test_folder, mock_folder_response @@ -22,30 +24,42 @@ def test_update_info(test_item_and_response, mock_box_session, etag, if_match_he mock_box_session.put.return_value = mock_item_response data = {'foo': 'bar', 'baz': {'foo': 'bar'}, 'num': 4} update_response = test_item.update_info(data=data, etag=etag) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=if_match_header, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=if_match_header, params=None + ) assert isinstance(update_response, test_item.__class__) assert update_response.object_id == test_item.object_id -def test_update_info_with_default_request_kwargs(test_item_and_response, mock_box_session, mock_box_session_2): +def test_update_info_with_default_request_kwargs( + test_item_and_response, mock_box_session, mock_box_session_2 +): # pylint:disable=redefined-outer-name, protected-access test_item, mock_item_response = test_item_and_response expected_url = test_item.get_url() - mock_box_session.with_default_network_request_kwargs.return_value = mock_box_session_2 + mock_box_session.with_default_network_request_kwargs.return_value = ( + mock_box_session_2 + ) mock_box_session_2.put.return_value = mock_item_response data = {'foo': 'bar', 'baz': {'foo': 'bar'}, 'num': 4} extra_network_parameters = {'timeout': 1} - update_response = test_item.update_info(data=data, extra_network_parameters=extra_network_parameters) - mock_box_session.with_default_network_request_kwargs.assert_called_once_with({'timeout': 1}) - mock_box_session_2.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + update_response = test_item.update_info( + data=data, extra_network_parameters=extra_network_parameters + ) + mock_box_session.with_default_network_request_kwargs.assert_called_once_with( + {'timeout': 1} + ) + mock_box_session_2.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(update_response, test_item.__class__) assert update_response.object_id == test_item.object_id def test_clear_unshared_at_for_shared_link( - test_item_and_response, - mock_box_session, - test_url, + test_item_and_response, + mock_box_session, + test_url, ): test_item, _ = test_item_and_response expected_url = test_item.get_url() @@ -57,7 +71,11 @@ def test_clear_unshared_at_for_shared_link( 'unshared_at': None, }, } - expected_data = {'shared_link': {'unshared_at': None, }, } + expected_data = { + 'shared_link': { + 'unshared_at': None, + }, + } shared_link = test_item.get_shared_link(unshared_at=None) mock_box_session.put.assert_called_once_with( expected_url, @@ -68,7 +86,9 @@ def test_clear_unshared_at_for_shared_link( assert shared_link is test_url -def test_remove_shared_link(test_item_and_response, mock_box_session, etag, if_match_header): +def test_remove_shared_link( + test_item_and_response, mock_box_session, etag, if_match_header +): # pylint:disable=redefined-outer-name, protected-access test_item, _ = test_item_and_response expected_url = test_item.get_url() @@ -88,14 +108,23 @@ def test_remove_shared_link(test_item_and_response, mock_box_session, etag, if_m @pytest.mark.parametrize('fields', (None, ['name', 'created_at'])) -def test_get(test_item_and_response, mock_box_session, fields, mock_object_id, etag, if_none_match_header): +def test_get( + test_item_and_response, + mock_box_session, + fields, + mock_object_id, + etag, + if_none_match_header, +): # pylint:disable=redefined-outer-name, protected-access test_item, mock_item_response = test_item_and_response expected_url = test_item.get_url() mock_box_session.get.return_value = mock_item_response expected_params = {'fields': ','.join(fields)} if fields else None info = test_item.get(fields=fields, etag=etag) - mock_box_session.get.assert_called_once_with(expected_url, params=expected_params, headers=if_none_match_header) + mock_box_session.get.assert_called_once_with( + expected_url, params=expected_params, headers=if_none_match_header + ) assert isinstance(info, test_item.__class__) assert info.id == mock_object_id @@ -104,7 +133,9 @@ def test_get_watermark(test_item_and_response, mock_box_session): test_item, _ = test_item_and_response created_at = '2016-10-31T15:33:33-07:00' modified_at = '2016-10-31T15:33:33-07:00' - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/watermark' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/watermark' + ) mock_box_session.get.return_value.json.return_value = { 'watermark': { 'created_at': created_at, @@ -122,7 +153,9 @@ def test_apply_watermark(test_item_and_response, mock_box_session): test_item, _ = test_item_and_response created_at = '2016-10-31T15:33:33-07:00' modified_at = '2016-10-31T15:33:33-07:00' - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/watermark' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/watermark' + ) mock_box_session.put.return_value.json.return_value = { 'watermark': { 'created_at': created_at, @@ -130,7 +163,9 @@ def test_apply_watermark(test_item_and_response, mock_box_session): }, } watermark = test_item.apply_watermark() - mock_box_session.put.assert_called_once_with(expected_url, data='{"watermark": {"imprint": "default"}}') + mock_box_session.put.assert_called_once_with( + expected_url, data='{"watermark": {"imprint": "default"}}' + ) assert isinstance(watermark, Watermark) assert watermark['created_at'] == created_at assert watermark['modified_at'] == modified_at @@ -138,10 +173,14 @@ def test_apply_watermark(test_item_and_response, mock_box_session): def test_delete_watermark(test_item_and_response, mock_box_session): test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/watermark' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/watermark' + ) mock_box_session.delete.return_value.ok = True is_watermark_deleted = test_item.delete_watermark() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False + ) assert is_watermark_deleted is True @@ -166,26 +205,52 @@ def test_collaborate_with_group(test_item_and_response, test_group, mock_box_ses 'created_by': { 'type': 'user', 'id': '1111', - } + }, } mock_box_session.post.return_value.json.return_value = mock_collaboration collaboration = test_item.collaborate(test_group, 'editor') - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data), params={}) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data), params={} + ) assert collaboration.id == mock_collaboration['id'] assert collaboration['type'] == mock_collaboration['type'] assert collaboration['created_by']['id'] == mock_collaboration['created_by']['id'] -@pytest.mark.parametrize('can_view_path,fields,notify,data,params', [ - (None, None, None, {}, {}), - (True, None, None, {'can_view_path': True}, {}), - (False, None, None, {'can_view_path': False}, {}), - (None, ['type', 'id', 'created_by'], None, {}, {'fields': 'type,id,created_by'}), - (None, None, True, {}, {'notify': True}), - (None, None, False, {}, {'notify': False}), - (True, ['type', 'id', 'created_by'], False, {'can_view_path': True}, {'fields': 'type,id,created_by', 'notify': False}) -]) -def test_collaborate_with_user(test_item_and_response, mock_user, mock_box_session, can_view_path, fields, notify, data, params): +@pytest.mark.parametrize( + 'can_view_path,fields,notify,data,params', + [ + (None, None, None, {}, {}), + (True, None, None, {'can_view_path': True}, {}), + (False, None, None, {'can_view_path': False}, {}), + ( + None, + ['type', 'id', 'created_by'], + None, + {}, + {'fields': 'type,id,created_by'}, + ), + (None, None, True, {}, {'notify': True}), + (None, None, False, {}, {'notify': False}), + ( + True, + ['type', 'id', 'created_by'], + False, + {'can_view_path': True}, + {'fields': 'type,id,created_by', 'notify': False}, + ), + ], +) +def test_collaborate_with_user( + test_item_and_response, + mock_user, + mock_box_session, + can_view_path, + fields, + notify, + data, + params, +): # pylint:disable=redefined-outer-name, protected-access test_item, _ = test_item_and_response expected_url = f'{API.BASE_API_URL}/collaborations' @@ -207,27 +272,54 @@ def test_collaborate_with_user(test_item_and_response, mock_user, mock_box_sessi 'created_by': { 'type': 'user', 'id': '1111', - } + }, } expected_params = params mock_box_session.post.return_value.json.return_value = mock_collaboration - collaboration = test_item.collaborate(mock_user, 'editor', can_view_path=can_view_path, fields=fields, notify=notify) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data), params=expected_params) + collaboration = test_item.collaborate( + mock_user, 'editor', can_view_path=can_view_path, fields=fields, notify=notify + ) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data), params=expected_params + ) assert collaboration.id == mock_collaboration['id'] assert collaboration['type'] == mock_collaboration['type'] assert collaboration['created_by']['id'] == mock_collaboration['created_by']['id'] -@pytest.mark.parametrize('can_view_path,fields,notify,data,params', [ - (None, None, None, {}, {}), - (True, None, None, {'can_view_path': True}, {}), - (False, None, None, {'can_view_path': False}, {}), - (None, ['type', 'id', 'created_by'], None, {}, {'fields': 'type,id,created_by'}), - (None, None, True, {}, {'notify': True}), - (None, None, False, {}, {'notify': False}), - (True, ['type', 'id', 'created_by'], False, {'can_view_path': True}, {'fields': 'type,id,created_by', 'notify': False}) -]) -def test_collaborate_with_login(test_item_and_response, mock_box_session, can_view_path, fields, notify, data, params): +@pytest.mark.parametrize( + 'can_view_path,fields,notify,data,params', + [ + (None, None, None, {}, {}), + (True, None, None, {'can_view_path': True}, {}), + (False, None, None, {'can_view_path': False}, {}), + ( + None, + ['type', 'id', 'created_by'], + None, + {}, + {'fields': 'type,id,created_by'}, + ), + (None, None, True, {}, {'notify': True}), + (None, None, False, {}, {'notify': False}), + ( + True, + ['type', 'id', 'created_by'], + False, + {'can_view_path': True}, + {'fields': 'type,id,created_by', 'notify': False}, + ), + ], +) +def test_collaborate_with_login( + test_item_and_response, + mock_box_session, + can_view_path, + fields, + notify, + data, + params, +): # pylint:disable=redefined-outer-name, protected-access test_item, _ = test_item_and_response expected_url = f'{API.BASE_API_URL}/collaborations' @@ -249,12 +341,20 @@ def test_collaborate_with_login(test_item_and_response, mock_box_session, can_vi 'created_by': { 'type': 'user', 'id': '1111', - } + }, } expected_params = params mock_box_session.post.return_value.json.return_value = mock_collaboration - collaboration = test_item.collaborate_with_login('test@example.com', 'editor', can_view_path=can_view_path, fields=fields, notify=notify) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data), params=expected_params) + collaboration = test_item.collaborate_with_login( + 'test@example.com', + 'editor', + can_view_path=can_view_path, + fields=fields, + notify=notify, + ) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data), params=expected_params + ) assert collaboration.id == mock_collaboration['id'] assert collaboration['type'] == mock_collaboration['type'] assert collaboration['created_by']['id'] == mock_collaboration['created_by']['id'] @@ -274,7 +374,7 @@ def test_collaborations(test_item_and_response, mock_box_session): } mock_box_session.get.return_value.json.return_value = { 'limit': 500, - 'entries': [mock_collaboration] + 'entries': [mock_collaboration], } collaborations = test_item.get_collaborations(limit=500) collaboration = collaborations.next() @@ -288,7 +388,9 @@ def test_collaborations(test_item_and_response, mock_box_session): def test_get_all_metadata(test_item_and_response, mock_box_session): test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/metadata' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}/metadata' + ) mock_metadata = { 'currentDocumentStage': 'prioritization', 'needsApprovalFrom': 'planning team', @@ -303,7 +405,7 @@ def test_get_all_metadata(test_item_and_response, mock_box_session): } mock_box_session.get.return_value.json.return_value = { 'limit': 100, - 'entries': [mock_metadata] + 'entries': [mock_metadata], } all_metadata = test_item.get_all_metadata() @@ -318,42 +420,50 @@ def test_get_all_metadata(test_item_and_response, mock_box_session): def test_add_classification(test_item_and_response, mock_box_session): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) metadata_response = { 'Box__Security__Classification__Key': 'Public', } - metadata_response = mock_box_session.post.return_value.json.return_value = metadata_response - data = { - 'Box__Security__Classification__Key': 'Public' - } - headers = { - b'Content-Type': b'application/json' - } + metadata_response = mock_box_session.post.return_value.json.return_value = ( + metadata_response + ) + data = {'Box__Security__Classification__Key': 'Public'} + headers = {b'Content-Type': b'application/json'} metadata = test_item.add_classification('Public') - mock_box_session.post.assert_called_once_with(expected_url, headers=headers, data=json.dumps(data)) + mock_box_session.post.assert_called_once_with( + expected_url, headers=headers, data=json.dumps(data) + ) assert metadata is metadata_response['Box__Security__Classification__Key'] def test_update_classification(test_item_and_response, mock_box_session): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) metadata_response = { 'Box__Security__Classification__Key': 'Internal', } - metadata_response = mock_box_session.put.return_value.json.return_value = metadata_response - data = [{ - 'op': 'add', - 'path': '/Box__Security__Classification__Key', - 'value': 'Internal', - }] - headers = { - b'Content-Type': b'application/json-patch+json' - } + metadata_response = mock_box_session.put.return_value.json.return_value = ( + metadata_response + ) + data = [ + { + 'op': 'add', + 'path': '/Box__Security__Classification__Key', + 'value': 'Internal', + } + ] + headers = {b'Content-Type': b'application/json-patch+json'} metadata = test_item.update_classification('Internal') - mock_box_session.put.assert_called_once_with(expected_url, headers=headers, data=json.dumps(data)) + mock_box_session.put.assert_called_once_with( + expected_url, headers=headers, data=json.dumps(data) + ) assert metadata is metadata_response['Box__Security__Classification__Key'] @@ -363,55 +473,61 @@ def test_set_classification_succeeds(test_item_and_response, mock_box_session): metadata_response = { 'Box__Security__Classification__Key': 'Public', } - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) post_data = { 'Box__Security__Classification__Key': 'Public', } - put_data = [{ - 'op': 'add', - 'path': '/Box__Security__Classification__Key', - 'value': 'Public', - }] - post_headers = { - b'Content-Type': b'application/json' - } - put_headers = { - b'Content-Type': b'application/json-patch+json' - } + put_data = [ + { + 'op': 'add', + 'path': '/Box__Security__Classification__Key', + 'value': 'Public', + } + ] + post_headers = {b'Content-Type': b'application/json'} + put_headers = {b'Content-Type': b'application/json-patch+json'} mock_box_session.post.side_effect = [BoxAPIException(status=409)] mock_box_session.put.return_value.json.return_value = metadata_response metadata = test_item.set_classification('Public') - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(post_data), headers=post_headers) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(put_data), headers=put_headers) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(post_data), headers=post_headers + ) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(put_data), headers=put_headers + ) assert metadata is metadata_response['Box__Security__Classification__Key'] def test_set_classification_fails(test_item_and_response, mock_box_session): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) post_data = { 'Box__Security__Classification__Key': 'Public', } - post_headers = { - b'Content-Type': b'application/json' - } + post_headers = {b'Content-Type': b'application/json'} mock_box_session.post.side_effect = [BoxAPIException(status=500)] with pytest.raises(BoxAPIException): test_item.set_classification('Public') - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(post_data), headers=post_headers) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(post_data), headers=post_headers + ) def test_get_classification_succeeds(test_item_and_response, mock_box_session): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' - metadata_response = { - 'Box__Security__Classification__Key': 'Public' - } + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) + metadata_response = {'Box__Security__Classification__Key': 'Public'} mock_box_session.get.return_value.json.return_value = metadata_response metadata = test_item.get_classification() assert metadata is metadata_response['Box__Security__Classification__Key'] @@ -421,9 +537,13 @@ def test_get_classification_succeeds(test_item_and_response, mock_box_session): def test_get_classification_not_found(test_item_and_response, mock_box_session): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' - mock_box_session.get.side_effect = [BoxAPIException(status=404, code="instance_not_found")] + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) + mock_box_session.get.side_effect = [ + BoxAPIException(status=404, code="instance_not_found") + ] metadata = test_item.get_classification() assert metadata is None mock_box_session.get.assert_called_once_with(expected_url) @@ -432,20 +552,28 @@ def test_get_classification_not_found(test_item_and_response, mock_box_session): def test_get_classification_raises_exception(test_item_and_response, mock_box_session): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) mock_box_session.get.side_effect = [BoxAPIException(status=500)] with pytest.raises(BoxAPIException): test_item.get_classification() mock_box_session.get.assert_called_once_with(expected_url) -def test_remove_classification(test_item_and_response, mock_box_session, make_mock_box_request): +def test_remove_classification( + test_item_and_response, mock_box_session, make_mock_box_request +): # pylint:disable=redefined-outer-name test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' \ - f'/metadata/enterprise/securityClassification-6VMVochwUWo' - mock_box_session.delete.return_value, _ = make_mock_box_request(response_ok='success') + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type}s/{test_item.object_id}' + f'/metadata/enterprise/securityClassification-6VMVochwUWo' + ) + mock_box_session.delete.return_value, _ = make_mock_box_request( + response_ok='success' + ) is_removed = test_item.remove_classification() mock_box_session.delete.assert_called_once_with(expected_url) assert is_removed == 'success' diff --git a/test/unit/object/test_legal_hold.py b/test/unit/object/test_legal_hold.py index c4ee7916d..83fd485a1 100644 --- a/test/unit/object/test_legal_hold.py +++ b/test/unit/object/test_legal_hold.py @@ -4,7 +4,9 @@ def test_get(test_legal_hold, mock_box_session): file_version_id = '1234' - expected_url = f'{API.BASE_API_URL}/file_version_legal_holds/{test_legal_hold.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/file_version_legal_holds/{test_legal_hold.object_id}' + ) mock_box_session.get.return_value.json.return_value = { 'type': 'legal_hold', 'id': test_legal_hold.object_id, @@ -14,7 +16,9 @@ def test_get(test_legal_hold, mock_box_session): }, } file_version_legal_hold = test_legal_hold.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(file_version_legal_hold, LegalHold) assert file_version_legal_hold['file_version']['id'] == file_version_id assert file_version_legal_hold.type == 'legal_hold' diff --git a/test/unit/object/test_legal_hold_assignment.py b/test/unit/object/test_legal_hold_assignment.py index 9cec5bfef..14e927dc1 100644 --- a/test/unit/object/test_legal_hold_assignment.py +++ b/test/unit/object/test_legal_hold_assignment.py @@ -17,10 +17,15 @@ def test_get(test_legal_hold_policy_assignment, mock_box_session): }, } legal_hold_policy_assignment = test_legal_hold_policy_assignment.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(legal_hold_policy_assignment, LegalHoldPolicyAssignment) assert legal_hold_policy_assignment.type == 'legal_hold_policy_assignment' - assert legal_hold_policy_assignment.object_id == test_legal_hold_policy_assignment.object_id + assert ( + legal_hold_policy_assignment.object_id + == test_legal_hold_policy_assignment.object_id + ) assert legal_hold_policy_assignment['assigned_to']['type'] == 'user' assert legal_hold_policy_assignment['assigned_to']['id'] == '1234' @@ -34,9 +39,9 @@ def delete_assignment_response(): def test_delete_policy_return_the_correct_response( - test_legal_hold_policy_assignment, - mock_box_session, - delete_assignment_response, + test_legal_hold_policy_assignment, + mock_box_session, + delete_assignment_response, ): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value = delete_assignment_response @@ -44,5 +49,7 @@ def test_delete_policy_return_the_correct_response( # pylint:disable=protected-access expected_url = test_legal_hold_policy_assignment.get_url() # pylint:enable = protected-access - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True diff --git a/test/unit/object/test_legal_hold_policy.py b/test/unit/object/test_legal_hold_policy.py index 9d96c8380..28341d4a4 100644 --- a/test/unit/object/test_legal_hold_policy.py +++ b/test/unit/object/test_legal_hold_policy.py @@ -35,7 +35,7 @@ def policies_response(policy_id_1, policy_id_2): mock_network_response.json.return_value = { 'entries': [ {'type': 'legal_hold_policy', 'id': policy_id_1, 'name': 'Test Policy 1'}, - {'type': 'legal_hold_policy', 'id': policy_id_2, 'name': 'Test Policy 2'} + {'type': 'legal_hold_policy', 'id': policy_id_2, 'name': 'Test Policy 2'}, ], 'limit': 5, } @@ -49,7 +49,7 @@ def legal_hold_response(legal_hold_id_1, legal_hold_id_2): mock_network_response.json.return_value = { 'entries': [ {'type': 'legal_hold', 'id': legal_hold_id_1, 'name': 'Test Legal Hold 1'}, - {'type': 'legal_hold', 'id': legal_hold_id_2, 'name': 'Test Legal Hold 2'} + {'type': 'legal_hold', 'id': legal_hold_id_2, 'name': 'Test Legal Hold 2'}, ], 'limit': 5, } @@ -62,10 +62,7 @@ def test_assign(test_legal_hold_policy, mock_box_session, test_file): expected_url = f'{API.BASE_API_URL}/legal_hold_policy_assignments' expected_body = { 'policy_id': test_legal_hold_policy.object_id, - 'assign_to': { - 'type': 'file', - 'id': test_file.object_id - } + 'assign_to': {'type': 'file', 'id': test_file.object_id}, } mock_box_session.post.return_value.json.return_value = { 'type': 'legal_hold_policy_assignment', @@ -73,7 +70,9 @@ def test_assign(test_legal_hold_policy, mock_box_session, test_file): 'assigned_at': assigned_at, } new_legal_hold_assignment = test_legal_hold_policy.assign(test_file) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(new_legal_hold_assignment, LegalHoldPolicyAssignment) assert new_legal_hold_assignment.assigned_at == assigned_at assert new_legal_hold_assignment.id == assignment_id @@ -81,14 +80,18 @@ def test_assign(test_legal_hold_policy, mock_box_session, test_file): def test_get(test_legal_hold_policy, mock_box_session): created_at = '2016-05-18T17:38:03-07:00' - expected_url = f'{API.BASE_API_URL}/legal_hold_policies/{test_legal_hold_policy.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/legal_hold_policies/{test_legal_hold_policy.object_id}' + ) mock_box_session.get.return_value.json.return_value = { 'type': 'legal_hold_policy', 'id': test_legal_hold_policy.object_id, - 'created_at': created_at + 'created_at': created_at, } legal_hold_policy = test_legal_hold_policy.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(legal_hold_policy, LegalHoldPolicy) assert legal_hold_policy.created_at == created_at @@ -102,9 +105,9 @@ def delete_policy_response(): def test_delete_policy_return_the_correct_response( - test_legal_hold_policy, - mock_box_session, - delete_policy_response, + test_legal_hold_policy, + mock_box_session, + delete_policy_response, ): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value = delete_policy_response @@ -112,31 +115,38 @@ def test_delete_policy_return_the_correct_response( # pylint:disable=protected-access expected_url = test_legal_hold_policy.get_url() # pylint:enable = protected-access - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True -@pytest.mark.parametrize('assign_to_type, assign_to_id, params', [ - (None, None, {}), - ('file', None, {'assign_to_type': 'file'}), - ('folder', '22222', {'assign_to_type': 'folder', 'assign_to_id': '22222'}) -]) +@pytest.mark.parametrize( + 'assign_to_type, assign_to_id, params', + [ + (None, None, {}), + ('file', None, {'assign_to_type': 'file'}), + ('folder', '22222', {'assign_to_type': 'folder', 'assign_to_id': '22222'}), + ], +) def test_get_assignments( - test_legal_hold_policy, - mock_box_session, - policies_response, - policy_id_1, - policy_id_2, - assign_to_type, - assign_to_id, - params, + test_legal_hold_policy, + mock_box_session, + policies_response, + policy_id_1, + policy_id_2, + assign_to_type, + assign_to_id, + params, ): # pylint:disable=redefined-outer-name expected_url = f'{API.BASE_API_URL}/legal_hold_policy_assignments' expected_params = {'policy_id': test_legal_hold_policy.object_id} expected_params.update(params) mock_box_session.get.return_value = policies_response - assignments = test_legal_hold_policy.get_assignments(assign_to_type=assign_to_type, assign_to_id=assign_to_id) + assignments = test_legal_hold_policy.get_assignments( + assign_to_type=assign_to_type, assign_to_id=assign_to_id + ) for assignment, expected_id in zip(assignments, [policy_id_1, policy_id_2]): assert assignment.object_id == expected_id # pylint:disable=protected-access @@ -145,11 +155,11 @@ def test_get_assignments( def test_get_file_version_legal_holds( - test_legal_hold_policy, - mock_box_session, - legal_hold_response, - legal_hold_id_1, - legal_hold_id_2 + test_legal_hold_policy, + mock_box_session, + legal_hold_response, + legal_hold_id_1, + legal_hold_id_2, ): # pylint:disable=redefined-outer-name mock_box_session.get.return_value = legal_hold_response diff --git a/test/unit/object/test_metadata.py b/test/unit/object/test_metadata.py index 592296e9d..d81f30cdc 100644 --- a/test/unit/object/test_metadata.py +++ b/test/unit/object/test_metadata.py @@ -37,7 +37,14 @@ def test_object(test_file, test_folder, request): @pytest.mark.parametrize('success', [True, False]) -def test_delete(mock_box_session, make_mock_box_request, test_object, metadata_scope, metadata_template, success): +def test_delete( + mock_box_session, + make_mock_box_request, + test_object, + metadata_scope, + metadata_template, + success, +): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value, _ = make_mock_box_request(response_ok=success) metadata = test_object.metadata(metadata_scope, metadata_template) @@ -46,15 +53,17 @@ def test_delete(mock_box_session, make_mock_box_request, test_object, metadata_s def test_create( - mock_box_session, - make_mock_box_request, - test_object, - metadata_scope, - metadata_template, - metadata_response, + mock_box_session, + make_mock_box_request, + test_object, + metadata_scope, + metadata_template, + metadata_response, ): # pylint:disable=redefined-outer-name - mock_box_session.post.return_value, _ = make_mock_box_request(response=metadata_response) + mock_box_session.post.return_value, _ = make_mock_box_request( + response=metadata_response + ) metadata = test_object.metadata(metadata_scope, metadata_template) response = metadata.create(metadata_response) assert response is metadata_response @@ -66,40 +75,56 @@ def test_create( def test_set( - mock_box_session, - test_object, - metadata_scope, - metadata_template, - metadata_response, + mock_box_session, + test_object, + metadata_scope, + metadata_template, + metadata_response, ): post_data = { 'case_status': 'in-progress', } post_value = json.dumps(post_data) - put_value = json.dumps([{ - 'op': 'add', - 'path': '/case_status', - 'value': 'in-progress', - }]) - mock_box_session.post.side_effect = [BoxAPIException(status=409, message="Conflict")] + put_value = json.dumps( + [ + { + 'op': 'add', + 'path': '/case_status', + 'value': 'in-progress', + } + ] + ) + mock_box_session.post.side_effect = [ + BoxAPIException(status=409, message="Conflict") + ] mock_box_session.put.return_value.json.return_value = metadata_response metadata = test_object.metadata(metadata_scope, metadata_template) response = metadata.set(post_data) assert response is metadata_response - mock_box_session.post.assert_called_once_with(metadata.get_url(), data=post_value, headers={b'Content-Type': b'application/json'}) - mock_box_session.put.assert_called_once_with(metadata.get_url(), data=put_value, headers={b'Content-Type': b'application/json-patch+json'}) + mock_box_session.post.assert_called_once_with( + metadata.get_url(), + data=post_value, + headers={b'Content-Type': b'application/json'}, + ) + mock_box_session.put.assert_called_once_with( + metadata.get_url(), + data=put_value, + headers={b'Content-Type': b'application/json-patch+json'}, + ) def test_get( - mock_box_session, - make_mock_box_request, - test_object, - metadata_scope, - metadata_template, - metadata_response, + mock_box_session, + make_mock_box_request, + test_object, + metadata_scope, + metadata_template, + metadata_response, ): # pylint:disable=redefined-outer-name - mock_box_session.get.return_value, _ = make_mock_box_request(response=metadata_response) + mock_box_session.get.return_value, _ = make_mock_box_request( + response=metadata_response + ) metadata = test_object.metadata(metadata_scope, metadata_template) response = metadata.get() assert response is metadata_response @@ -117,16 +142,18 @@ def metadata_update(): def test_update( - mock_box_session, - make_mock_box_request, - test_object, - metadata_scope, - metadata_template, - metadata_response, - metadata_update, + mock_box_session, + make_mock_box_request, + test_object, + metadata_scope, + metadata_template, + metadata_response, + metadata_update, ): # pylint:disable=redefined-outer-name - mock_box_session.put.return_value, _ = make_mock_box_request(response=metadata_response) + mock_box_session.put.return_value, _ = make_mock_box_request( + response=metadata_response + ) metadata = test_object.metadata(metadata_scope, metadata_template) response = metadata.update(metadata_update) assert response is metadata_response diff --git a/test/unit/object/test_metadata_cascade_policy.py b/test/unit/object/test_metadata_cascade_policy.py index 8e23a44d6..fa62368cc 100644 --- a/test/unit/object/test_metadata_cascade_policy.py +++ b/test/unit/object/test_metadata_cascade_policy.py @@ -4,7 +4,10 @@ from boxsdk.config import API from boxsdk.object.enterprise import Enterprise from boxsdk.object.folder import Folder -from boxsdk.object.metadata_cascade_policy import MetadataCascadePolicy, CascadePolicyConflictResolution +from boxsdk.object.metadata_cascade_policy import ( + MetadataCascadePolicy, + CascadePolicyConflictResolution, +) @pytest.fixture() @@ -13,7 +16,9 @@ def test_cascade_policy(mock_box_session): def test_get(test_cascade_policy, mock_box_session): - expected_url = f'{API.BASE_API_URL}/metadata_cascade_policies/{test_cascade_policy.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/metadata_cascade_policies/{test_cascade_policy.object_id}' + ) mock_box_session.get.return_value.json.return_value = { 'id': '84113349-794d-445c-b93c-d8481b223434', 'type': 'metadata_cascade_policy', @@ -31,7 +36,9 @@ def test_get(test_cascade_policy, mock_box_session): cascade_policy = test_cascade_policy.get() - mock_box_session.get.assert_called_once_with(expected_url, params=None, headers=None) + mock_box_session.get.assert_called_once_with( + expected_url, params=None, headers=None + ) assert isinstance(cascade_policy, MetadataCascadePolicy) enterprise = cascade_policy.owner_enterprise assert isinstance(enterprise, Enterprise) @@ -44,12 +51,16 @@ def test_get(test_cascade_policy, mock_box_session): def test_delete(test_cascade_policy, mock_box_session): - expected_url = f'{API.BASE_API_URL}/metadata_cascade_policies/{test_cascade_policy.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/metadata_cascade_policies/{test_cascade_policy.object_id}' + ) mock_box_session.delete.return_value.ok = True result = test_cascade_policy.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={}) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=None, params={} + ) assert result is True @@ -63,8 +74,6 @@ def test_force_apply(test_cascade_policy, mock_box_session): result = test_cascade_policy.force_apply(CascadePolicyConflictResolution.OVERWRITE) mock_box_session.post.assert_called_once_with( - expected_url, - data=json.dumps(expected_body), - expect_json_response=False + expected_url, data=json.dumps(expected_body), expect_json_response=False ) assert result is True diff --git a/test/unit/object/test_metadata_template.py b/test/unit/object/test_metadata_template.py index 2c0c079a9..77a031235 100644 --- a/test/unit/object/test_metadata_template.py +++ b/test/unit/object/test_metadata_template.py @@ -1,7 +1,11 @@ import json from boxsdk.config import API -from boxsdk.object.metadata_template import MetadataTemplate, MetadataField, MetadataFieldType +from boxsdk.object.metadata_template import ( + MetadataTemplate, + MetadataField, + MetadataFieldType, +) def test_get(test_metadata_template, mock_box_session): @@ -32,7 +36,9 @@ def test_get(test_metadata_template, mock_box_session): template = test_metadata_template.get() - mock_box_session.get.assert_called_once_with(expected_url, params=None, headers=None) + mock_box_session.get.assert_called_once_with( + expected_url, params=None, headers=None + ) assert isinstance(template, MetadataTemplate) assert template.object_id is None assert template.displayName == 'Vendor Contract' @@ -50,7 +56,9 @@ def test_delete(test_metadata_template, mock_box_session): result = test_metadata_template.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={}) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=None, params={} + ) assert result is True @@ -59,7 +67,12 @@ def test_update_info(test_metadata_template, mock_box_session): updates = test_metadata_template.start_update() updates.add_enum_option('state', 'WI') updates.add_field( - MetadataField(field_type=MetadataFieldType.STRING, display_name='Name', description="Description", hidden=False) + MetadataField( + field_type=MetadataFieldType.STRING, + display_name='Name', + description="Description", + hidden=False, + ) ) updates.reorder_enum_options('state', ['CA', 'NY', 'TX', 'WI']) updates.reorder_fields(['bday', 'name', 'state']) @@ -81,7 +94,7 @@ def test_update_info(test_metadata_template, mock_box_session): 'type': 'string', 'displayName': 'Name', "description": "Description", - "hidden": False + "hidden": False, }, }, { @@ -134,7 +147,7 @@ def test_update_info(test_metadata_template, mock_box_session): 'key': 'name', 'displayName': 'Name', 'description': 'Description', - 'hidden': False + 'hidden': False, }, { 'type': 'enum', @@ -152,7 +165,9 @@ def test_update_info(test_metadata_template, mock_box_session): updated_template = test_metadata_template.update_info(updates=updates) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_body), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(expected_body), headers=None, params=None + ) assert isinstance(updated_template, MetadataTemplate) assert updated_template.hidden is False assert updated_template.object_id is None diff --git a/test/unit/object/test_retention_policy.py b/test/unit/object/test_retention_policy.py index 0c50639fb..8eb876f89 100644 --- a/test/unit/object/test_retention_policy.py +++ b/test/unit/object/test_retention_policy.py @@ -6,7 +6,9 @@ def test_get(test_retention_policy, mock_box_session): - expected_url = f'{API.BASE_API_URL}/retention_policies/{test_retention_policy.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/retention_policies/{test_retention_policy.object_id}' + ) mock_box_session.get.return_value.json.return_value = { 'type': test_retention_policy.object_type, 'id': test_retention_policy.object_id, @@ -17,7 +19,9 @@ def test_get(test_retention_policy, mock_box_session): 'description': 'Description', } retention_policy = test_retention_policy.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(retention_policy, RetentionPolicy) assert retention_policy['type'] == test_retention_policy.object_type assert retention_policy['id'] == test_retention_policy.object_id @@ -30,7 +34,9 @@ def test_update(test_retention_policy, mock_box_session): new_retention_type = 'modifiable' new_retention_length = 60 new_description = 'New Description' - expected_url = f'{API.BASE_API_URL}/retention_policies/{test_retention_policy.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/retention_policies/{test_retention_policy.object_id}' + ) mock_box_session.put.return_value.json.return_value = { 'type': test_retention_policy.object_type, 'id': test_retention_policy.object_id, @@ -47,7 +53,9 @@ def test_update(test_retention_policy, mock_box_session): 'description': new_description, } retention_policy = test_retention_policy.update_info(data=data) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(retention_policy, RetentionPolicy) assert retention_policy['type'] == test_retention_policy.object_type assert retention_policy['id'] == test_retention_policy.object_id @@ -66,7 +74,7 @@ def test_assign(test_retention_policy, test_folder, mock_box_session): 'type': test_folder.object_type, 'id': test_folder.object_id, }, - "start_date_field": "upload_date" + "start_date_field": "upload_date", } mock_assignment = { 'type': 'retention_policy_assignment', @@ -75,13 +83,19 @@ def test_assign(test_retention_policy, test_folder, mock_box_session): 'type': 'retention_policy', 'id': policy_id, }, - "start_date_field": "upload_date" + "start_date_field": "upload_date", } mock_box_session.post.return_value.json.return_value = mock_assignment - assignment = test_retention_policy.assign(test_folder, start_date_field='upload_date') - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data), params={}) + assignment = test_retention_policy.assign( + test_folder, start_date_field='upload_date' + ) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data), params={} + ) assert assignment.id == mock_assignment['id'] - assert assignment.retention_policy['id'] == mock_assignment['retention_policy']['id'] + assert ( + assignment.retention_policy['id'] == mock_assignment['retention_policy']['id'] + ) assert assignment.start_date_field == mock_assignment['start_date_field'] assert isinstance(assignment, RetentionPolicyAssignment) diff --git a/test/unit/object/test_retention_policy_assignment.py b/test/unit/object/test_retention_policy_assignment.py index fbab67418..050ce8631 100644 --- a/test/unit/object/test_retention_policy_assignment.py +++ b/test/unit/object/test_retention_policy_assignment.py @@ -11,13 +11,22 @@ def test_get_assignment(test_retention_policy_assignment, mock_box_session): 'id': test_retention_policy_assignment.object_id, } retention_policy_assignment = test_retention_policy_assignment.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(retention_policy_assignment, RetentionPolicyAssignment) - assert retention_policy_assignment['type'] == test_retention_policy_assignment.object_type - assert retention_policy_assignment['id'] == test_retention_policy_assignment.object_id + assert ( + retention_policy_assignment['type'] + == test_retention_policy_assignment.object_type + ) + assert ( + retention_policy_assignment['id'] == test_retention_policy_assignment.object_id + ) -def test_get_files_under_retention(test_retention_policy_assignment, test_file, mock_box_session): +def test_get_files_under_retention( + test_retention_policy_assignment, test_file, mock_box_session +): # given: test_marker = 'test_marker' test_limit = 50 @@ -31,8 +40,7 @@ def test_get_files_under_retention(test_retention_policy_assignment, test_file, # when: files_under_retention = test_retention_policy_assignment.get_files_under_retention( - limit=test_limit, - marker=test_marker + limit=test_limit, marker=test_marker ) file_under_retention = files_under_retention.next() @@ -46,7 +54,9 @@ def test_get_files_under_retention(test_retention_policy_assignment, test_file, assert file_under_retention.object_id == test_file.object_id -def test_get_file_versions_under_retention(test_retention_policy_assignment, test_file_version, mock_box_session): +def test_get_file_versions_under_retention( + test_retention_policy_assignment, test_file_version, mock_box_session +): # given: test_marker = 'test_marker' test_limit = 50 @@ -59,9 +69,10 @@ def test_get_file_versions_under_retention(test_retention_policy_assignment, tes } # when: - file_versions_under_retention = test_retention_policy_assignment.get_file_versions_under_retention( - limit=test_limit, - marker=test_marker + file_versions_under_retention = ( + test_retention_policy_assignment.get_file_versions_under_retention( + limit=test_limit, marker=test_marker + ) ) file_version_under_retention = file_versions_under_retention.next() @@ -80,6 +91,8 @@ def test_delete_assignment(test_retention_policy_assignment, mock_box_session): mock_box_session.delete.return_value.ok = True is_assignment_deleted = test_retention_policy_assignment.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, params={}, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, params={}, headers=None + ) assert is_assignment_deleted diff --git a/test/unit/object/test_search.py b/test/unit/object/test_search.py index 8c5e9d5fa..69432fc74 100644 --- a/test/unit/object/test_search.py +++ b/test/unit/object/test_search.py @@ -5,7 +5,12 @@ from boxsdk.config import API from boxsdk.object.file import File from boxsdk.object.user import User -from boxsdk.object.search import MetadataSearchFilters, MetadataSearchFilter, SearchScope, TrashContent +from boxsdk.object.search import ( + MetadataSearchFilters, + MetadataSearchFilter, + SearchScope, + TrashContent, +) @pytest.fixture @@ -36,20 +41,26 @@ def search_content_types(request): @pytest.fixture def search_value_based_filters(): metadata_filters = MetadataSearchFilters() - metadata_filter = MetadataSearchFilter(template_key='mytemplate', scope='enterprise') + metadata_filter = MetadataSearchFilter( + template_key='mytemplate', scope='enterprise' + ) metadata_filter.add_value_based_filter(field_key='myfield', value='myvalue') metadata_filters.add_filter(metadata_filter) return metadata_filters -@pytest.fixture(params=( - {'gt_value': 'mygtvalue'}, - {'lt_value': 'myltvalue'}, - {'gt_value': 'mygtvalue', 'lt_value': 'myltvalue'} -)) +@pytest.fixture( + params=( + {'gt_value': 'mygtvalue'}, + {'lt_value': 'myltvalue'}, + {'gt_value': 'mygtvalue', 'lt_value': 'myltvalue'}, + ) +) def search_range_filters(request): metadata_filters = MetadataSearchFilters() - metadata_filter = MetadataSearchFilter(template_key='mytemplate', scope='enterprise') + metadata_filter = MetadataSearchFilter( + template_key='mytemplate', scope='enterprise' + ) filter_params = {'field_key': 'myfield'} filter_params.update(request.param) metadata_filter.add_range_filter(**filter_params) @@ -59,9 +70,7 @@ def search_range_filters(request): @pytest.fixture def search_entries(): - return [ - {'id': '1234', 'type': 'file'} - ] + return [{'id': '1234', 'type': 'file'}] @pytest.fixture @@ -70,7 +79,7 @@ def search_response(search_entries): 'entries': search_entries, 'total_count': len(search_entries), 'limit': 20, - 'offset': 0 + 'offset': 0, } @@ -89,10 +98,10 @@ def metadata_query_response(): '$version': 2, '$template': 'catalogImages', '$scope': 'enterprise_67890', - 'photographer': 'Bob Dylan' + 'photographer': 'Bob Dylan', } } - } + }, }, { 'type': 'folder', @@ -105,14 +114,14 @@ def metadata_query_response(): '$version': 2, '$template': 'catalogImages', '$scope': 'enterprise_67890', - 'photographer': 'Bob Dylan' + 'photographer': 'Bob Dylan', } } - } - } + }, + }, ], 'limit': 2, - 'next_marker': '' + 'next_marker': '', } @@ -122,25 +131,20 @@ def search_with_shared_links_entries(): { 'accessible_via_shared_link': 'https://www.box.com/s/vspke7y05sb214wjokpk', 'item': {'id': '1234', 'type': 'file'}, - 'type': 'search_result' + 'type': 'search_result', }, { 'accessible_via_shared_link': None, 'item': {'id': '1234', 'type': 'file'}, - 'type': 'search_result' - } + 'type': 'search_result', + }, ] @pytest.fixture def search_with_shared_links_response(search_with_shared_links_entries): entries = search_with_shared_links_entries - return { - 'entries': entries, - 'total_count': len(entries), - 'limit': 20, - 'offset': 0 - } + return {'entries': entries, 'total_count': len(entries), 'limit': 20, 'offset': 0} class Matcher: @@ -170,20 +174,22 @@ def compare_params(self, other): def test_query_with_value_based_filters( - mock_box_session, - make_mock_box_request, - test_search, - search_query, - search_limit, - search_offset, - search_value_based_filters, - search_response, - search_entries, - search_result_type, - search_content_types, + mock_box_session, + make_mock_box_request, + test_search, + search_query, + search_limit, + search_offset, + search_value_based_filters, + search_response, + search_entries, + search_result_type, + search_content_types, ): # pylint:disable=redefined-outer-name - mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response) + mock_box_session.get.return_value, _ = make_mock_box_request( + response=search_response + ) response = test_search.query( search_query, limit=search_limit, @@ -192,37 +198,48 @@ def test_query_with_value_based_filters( result_type=search_result_type, content_types=search_content_types, ) - for actual, expected in zip(response, [File(mock_box_session, item['id'], item) for item in search_entries]): + for actual, expected in zip( + response, [File(mock_box_session, item['id'], item) for item in search_entries] + ): assert actual == expected mock_box_session.get.assert_called_once_with( test_search.get_url(), - params=Matcher(compare_params, { - 'query': search_query, - 'limit': search_limit, - 'mdfilters': json.dumps(search_value_based_filters.as_list()), - 'offset': search_offset, - 'type': search_result_type, - 'content_types': ','.join(search_content_types) if search_content_types else search_content_types, - }) + params=Matcher( + compare_params, + { + 'query': search_query, + 'limit': search_limit, + 'mdfilters': json.dumps(search_value_based_filters.as_list()), + 'offset': search_offset, + 'type': search_result_type, + 'content_types': ( + ','.join(search_content_types) + if search_content_types + else search_content_types + ), + }, + ), ) def test_query_with_range_filters( - mock_box_session, - make_mock_box_request, - test_search, - search_query, - search_limit, - search_offset, - search_range_filters, - search_response, - search_entries, - search_result_type, - search_content_types, + mock_box_session, + make_mock_box_request, + test_search, + search_query, + search_limit, + search_offset, + search_range_filters, + search_response, + search_entries, + search_result_type, + search_content_types, ): # pylint:disable=redefined-outer-name - mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response) + mock_box_session.get.return_value, _ = make_mock_box_request( + response=search_response + ) response = test_search.query( search_query, limit=search_limit, @@ -231,56 +248,87 @@ def test_query_with_range_filters( result_type=search_result_type, content_types=search_content_types, ) - for actual, expected in zip(response, [File(mock_box_session, item['id'], item) for item in search_entries]): + for actual, expected in zip( + response, [File(mock_box_session, item['id'], item) for item in search_entries] + ): assert actual == expected mock_box_session.get.assert_called_once_with( test_search.get_url(), - params=Matcher(compare_params, { - 'query': search_query, - 'limit': search_limit, - 'mdfilters': json.dumps(search_range_filters.as_list()), - 'offset': search_offset, - 'type': search_result_type, - 'content_types': ','.join(search_content_types) if search_content_types else search_content_types, - }) + params=Matcher( + compare_params, + { + 'query': search_query, + 'limit': search_limit, + 'mdfilters': json.dumps(search_range_filters.as_list()), + 'offset': search_offset, + 'type': search_result_type, + 'content_types': ( + ','.join(search_content_types) + if search_content_types + else search_content_types + ), + }, + ), ) -@pytest.mark.parametrize('kwargs, params', [ - ({'scope': SearchScope.ENTERPRISE}, {'scope': 'enterprise_content'}), - ({'scope': SearchScope.USER}, {'scope': 'user_content'}), - ({'created_at_range': (None, '2018-01-01T00:00:00Z')}, {'created_at_range': ',2018-01-01T00:00:00Z'}), - ({'created_at_range': ('2015-02-03T12:00:00-08:00', None)}, {'created_at_range': '2015-02-03T12:00:00-08:00,'}), - ({'created_at_range': ('2012-01-01T00:00:00Z', '2012-12-31T11:59:59Z')}, {'created_at_range': '2012-01-01T00:00:00Z,2012-12-31T11:59:59Z'}), - ({'updated_at_range': (None, '2018-01-01T00:00:00Z')}, {'updated_at_range': ',2018-01-01T00:00:00Z'}), - ({'updated_at_range': ('2015-02-03T12:00:00-08:00', None)}, {'updated_at_range': '2015-02-03T12:00:00-08:00,'}), - ({'updated_at_range': ('2012-01-01T00:00:00Z', '2012-12-31T11:59:59Z')}, {'updated_at_range': '2012-01-01T00:00:00Z,2012-12-31T11:59:59Z'}), - ({'size_range': (None, 123)}, {'size_range': ',123'}), - ({'size_range': (123, None)}, {'size_range': '123,'}), - ({'size_range': (123, 456)}, {'size_range': '123,456'}), - ({'trash_content': TrashContent.NONE}, {'trash_content': 'non_trashed_only'}), - ({'trash_content': TrashContent.ONLY}, {'trash_content': 'trashed_only'}), - ({'sort': 'modified_at'}, {'sort': 'modified_at'}), - ({'direction': 'DESC'}, {'direction': 'DESC'}), -]) +@pytest.mark.parametrize( + 'kwargs, params', + [ + ({'scope': SearchScope.ENTERPRISE}, {'scope': 'enterprise_content'}), + ({'scope': SearchScope.USER}, {'scope': 'user_content'}), + ( + {'created_at_range': (None, '2018-01-01T00:00:00Z')}, + {'created_at_range': ',2018-01-01T00:00:00Z'}, + ), + ( + {'created_at_range': ('2015-02-03T12:00:00-08:00', None)}, + {'created_at_range': '2015-02-03T12:00:00-08:00,'}, + ), + ( + {'created_at_range': ('2012-01-01T00:00:00Z', '2012-12-31T11:59:59Z')}, + {'created_at_range': '2012-01-01T00:00:00Z,2012-12-31T11:59:59Z'}, + ), + ( + {'updated_at_range': (None, '2018-01-01T00:00:00Z')}, + {'updated_at_range': ',2018-01-01T00:00:00Z'}, + ), + ( + {'updated_at_range': ('2015-02-03T12:00:00-08:00', None)}, + {'updated_at_range': '2015-02-03T12:00:00-08:00,'}, + ), + ( + {'updated_at_range': ('2012-01-01T00:00:00Z', '2012-12-31T11:59:59Z')}, + {'updated_at_range': '2012-01-01T00:00:00Z,2012-12-31T11:59:59Z'}, + ), + ({'size_range': (None, 123)}, {'size_range': ',123'}), + ({'size_range': (123, None)}, {'size_range': '123,'}), + ({'size_range': (123, 456)}, {'size_range': '123,456'}), + ({'trash_content': TrashContent.NONE}, {'trash_content': 'non_trashed_only'}), + ({'trash_content': TrashContent.ONLY}, {'trash_content': 'trashed_only'}), + ({'sort': 'modified_at'}, {'sort': 'modified_at'}), + ({'direction': 'DESC'}, {'direction': 'DESC'}), + ], +) def test_query_with_optional_parameters( - mock_box_session, - test_search, - make_mock_box_request, - search_query, - search_response, - search_entries, - kwargs, - params + mock_box_session, + test_search, + make_mock_box_request, + search_query, + search_response, + search_entries, + kwargs, + params, ): # pylint:disable=redefined-outer-name - mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response) - response = test_search.query( - search_query, - **kwargs + mock_box_session.get.return_value, _ = make_mock_box_request( + response=search_response ) - for actual, expected in zip(response, [File(mock_box_session, item['id'], item) for item in search_entries]): + response = test_search.query(search_query, **kwargs) + for actual, expected in zip( + response, [File(mock_box_session, item['id'], item) for item in search_entries] + ): assert actual == expected expected_params = { @@ -289,44 +337,43 @@ def test_query_with_optional_parameters( expected_params.update(params) mock_box_session.get.assert_called_once_with( - test_search.get_url(), - params=Matcher(compare_params, expected_params) + test_search.get_url(), params=Matcher(compare_params, expected_params) ) def test_query_with_owner_users( - mock_box_session, - test_search, - make_mock_box_request, - search_query, - search_response, - search_entries, + mock_box_session, + test_search, + make_mock_box_request, + search_query, + search_response, + search_entries, ): # pylint:disable=redefined-outer-name user1 = User(mock_box_session, '987') user2 = User(mock_box_session, '654') - mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response) + mock_box_session.get.return_value, _ = make_mock_box_request( + response=search_response + ) response = test_search.query( search_query, owner_users=[user1, user2], ) - for actual, expected in zip(response, [File(mock_box_session, item['id'], item) for item in search_entries]): + for actual, expected in zip( + response, [File(mock_box_session, item['id'], item) for item in search_entries] + ): assert actual == expected mock_box_session.get.assert_called_once_with( test_search.get_url(), - params=Matcher(compare_params, { - 'query': search_query, - 'owner_user_ids': '987,654' - }) + params=Matcher( + compare_params, {'query': search_query, 'owner_user_ids': '987,654'} + ), ) def test_metadata_query( - mock_box_session, - make_mock_box_request, - test_search, - metadata_query_response + mock_box_session, make_mock_box_request, test_search, metadata_query_response ): # pylint:disable=redefined-outer-name expected_url = f'{API.BASE_API_URL}/metadata_queries/execute_read' @@ -334,12 +381,7 @@ def test_metadata_query( ancestor_folder_id = '5555' query = 'amount >= :arg' query_params = {'arg': 100} - order_by = [ - { - 'field_key': 'amount', - 'direction': 'asc' - } - ] + order_by = [{'field_key': 'amount', 'direction': 'asc'}] fields = ['type', 'id', 'name', 'metadata.enterprise_67890.catalogImages.$parent'] limit = 2 marker = 'AAAAAmVYB1FWec8GH6yWu2nwmanfMh07IyYInaa7DZDYjgO1H4KoLW29vPlLY173OKs' @@ -351,10 +393,12 @@ def test_metadata_query( 'query_params': query_params, 'order_by': order_by, 'marker': marker, - 'fields': fields + 'fields': fields, } expected_headers = {b'Content-Type': b'application/json'} - mock_box_session.post.return_value, _ = make_mock_box_request(response=metadata_query_response) + mock_box_session.post.return_value, _ = make_mock_box_request( + response=metadata_query_response + ) items = test_search.metadata_query( from_template=from_template, ancestor_folder_id=ancestor_folder_id, @@ -363,21 +407,31 @@ def test_metadata_query( order_by=order_by, marker=marker, limit=limit, - fields=fields + fields=fields, ) item1 = items.next() item2 = items.next() - mock_box_session.post.assert_called_once_with(expected_url, data=ANY, headers=expected_headers) + mock_box_session.post.assert_called_once_with( + expected_url, data=ANY, headers=expected_headers + ) assert dict(json.loads(mock_box_session.post.call_args[1]['data'])) == expected_data assert mock_box_session.post.call_args[1]['headers'] == expected_headers assert item1['type'] == 'file' - assert item1['metadata']['enterprise_67890']['catalogImages']['$parent'] == 'file_50347290' + assert ( + item1['metadata']['enterprise_67890']['catalogImages']['$parent'] + == 'file_50347290' + ) assert item2['type'] == 'folder' - assert item2['metadata']['enterprise_67890']['catalogImages']['$parent'] == 'file_50427291' + assert ( + item2['metadata']['enterprise_67890']['catalogImages']['$parent'] + == 'file_50427291' + ) def test_range_filter_without_gt_and_lt_will_fail_validation(): - metadata_filter = MetadataSearchFilter(template_key='mytemplate', scope='enterprise') + metadata_filter = MetadataSearchFilter( + template_key='mytemplate', scope='enterprise' + ) with pytest.raises(ValueError): metadata_filter.add_range_filter(field_key='mykey') @@ -398,20 +452,22 @@ def test_make_single_metadata_filter(test_search): def test_query_with_shared_links( - mock_box_session, - make_mock_box_request, - search_content_types, - search_limit, - search_offset, - search_query, - search_result_type, - search_value_based_filters, - search_with_shared_links_entries, - search_with_shared_links_response, - test_search, + mock_box_session, + make_mock_box_request, + search_content_types, + search_limit, + search_offset, + search_query, + search_result_type, + search_value_based_filters, + search_with_shared_links_entries, + search_with_shared_links_response, + test_search, ): # pylint:disable=redefined-outer-name - mock_box_session.get.return_value, _ = make_mock_box_request(response=search_with_shared_links_response) + mock_box_session.get.return_value, _ = make_mock_box_request( + response=search_with_shared_links_response + ) response = test_search.query_with_shared_links( search_query, limit=search_limit, @@ -421,18 +477,31 @@ def test_query_with_shared_links( content_types=search_content_types, ) - for actual, expected in zip(response, [File(mock_box_session, entry['item']['id'], entry['item']) for entry in search_with_shared_links_entries]): + for actual, expected in zip( + response, + [ + File(mock_box_session, entry['item']['id'], entry['item']) + for entry in search_with_shared_links_entries + ], + ): assert actual.item == expected mock_box_session.get.assert_called_once_with( test_search.get_url(), - params=Matcher(compare_params, { - 'query': search_query, - 'include_recent_shared_links': True, - 'limit': search_limit, - 'mdfilters': json.dumps(search_value_based_filters.as_list()), - 'offset': search_offset, - 'type': search_result_type, - 'content_types': ','.join(search_content_types) if search_content_types else search_content_types, - }) + params=Matcher( + compare_params, + { + 'query': search_query, + 'include_recent_shared_links': True, + 'limit': search_limit, + 'mdfilters': json.dumps(search_value_based_filters.as_list()), + 'offset': search_offset, + 'type': search_result_type, + 'content_types': ( + ','.join(search_content_types) + if search_content_types + else search_content_types + ), + }, + ), ) diff --git a/test/unit/object/test_sign_request.py b/test/unit/object/test_sign_request.py index b3a7909e1..0676c1d0f 100644 --- a/test/unit/object/test_sign_request.py +++ b/test/unit/object/test_sign_request.py @@ -25,14 +25,14 @@ def mock_sign_request_response(): 'type': 'folder', 'etag': '1', 'name': 'Contracts', - 'sequence_id': '3' + 'sequence_id': '3', }, 'prefill_tags': [ { 'document_tag_id': '1234', 'text_value': 'text', 'checkbox_value': 'true', - 'date_value': '2021-04-26T08:12:13.982Z' + 'date_value': '2021-04-26T08:12:13.982Z', } ], 'prepare_url': 'https://prepareurl.com', @@ -48,11 +48,11 @@ def mock_sign_request_response(): 'file_version': { 'id': '12345', 'type': 'file_version', - 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc' - } + 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc', + }, } ], - 'is_ready_for_download': 'true' + 'is_ready_for_download': 'true', }, 'signers': [ { @@ -64,7 +64,7 @@ def mock_sign_request_response(): 'has_viewed_document': 'true', 'signer_decision': { 'type': 'signed', - 'finalized_at': '2021-04-26T08:12:13.982Z' + 'finalized_at': '2021-04-26T08:12:13.982Z', }, 'inputs': [ { @@ -73,10 +73,10 @@ def mock_sign_request_response(): 'checkbox_value': 'true', 'date_value': '2021-04-26T08:12:13.982Z', 'type': 'text', - 'page_index': '4' + 'page_index': '4', } ], - 'embed_url': 'https://example.com' + 'embed_url': 'https://example.com', } ], 'signing_log': { @@ -86,11 +86,11 @@ def mock_sign_request_response(): 'file_version': { 'id': '12345', 'type': 'file_version', - 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc' + 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc', }, 'name': 'Contract.pdf', 'sequence_id': '3', - 'sha1': '85136C79CBF9FE36BB9D05D0639C70C265C18D37' + 'sha1': '85136C79CBF9FE36BB9D05D0639C70C265C18D37', }, 'source_files': [ { @@ -103,30 +103,37 @@ def mock_sign_request_response(): 'file_version': { 'id': '12345', 'type': 'file_version', - 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc' - } + 'sha1': '134b65991ed521fcfe4724b7d814ab8ded5185dc', + }, } ], 'status': 'cancelled', - 'template_id': '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb' + 'template_id': '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb', } return mock_sign_request -def test_get_sign_request(test_sign_request, mock_box_session, mock_sign_request_response): +def test_get_sign_request( + test_sign_request, mock_box_session, mock_sign_request_response +): expected_url = f'{API.BASE_API_URL}/sign_requests/{test_sign_request.object_id}' mock_box_session.get.return_value.json.return_value = mock_sign_request_response sign_request = test_sign_request.get() mock_box_session.get.assert_called_once_with( - expected_url, headers=None, params=None) + expected_url, headers=None, params=None + ) assert isinstance(sign_request, SignRequest) assert sign_request['id'] == test_sign_request.object_id -def test_cancel_sign_request(test_sign_request, mock_box_session, mock_sign_request_response): - expected_url = f'{API.BASE_API_URL}/sign_requests/{test_sign_request.object_id}/cancel' +def test_cancel_sign_request( + test_sign_request, mock_box_session, mock_sign_request_response +): + expected_url = ( + f'{API.BASE_API_URL}/sign_requests/{test_sign_request.object_id}/cancel' + ) mock_box_session.post.return_value.json.return_value = mock_sign_request_response sign_request = test_sign_request.cancel() @@ -138,9 +145,12 @@ def test_cancel_sign_request(test_sign_request, mock_box_session, mock_sign_requ def test_resend_sign_request(test_sign_request, mock_box_session): - expected_url = f'{API.BASE_API_URL}/sign_requests/{test_sign_request.object_id}/resend' + expected_url = ( + f'{API.BASE_API_URL}/sign_requests/{test_sign_request.object_id}/resend' + ) test_sign_request.resend() mock_box_session.post.assert_called_once_with( - expected_url, expect_json_response=False, skip_retry_codes={202}) + expected_url, expect_json_response=False, skip_retry_codes={202} + ) diff --git a/test/unit/object/test_sign_templates.py b/test/unit/object/test_sign_templates.py index 4742d2cf2..ef9b65504 100644 --- a/test/unit/object/test_sign_templates.py +++ b/test/unit/object/test_sign_templates.py @@ -11,13 +11,15 @@ def mock_sign_template_response(): "type": "sign-template", "name": "important-file.pdf", "email_message": "Please sign this document.\n\nKind regards", - "email_subject": "Box User (boxuser@box.com) has requested your signature on a document", + "email_subject": ( + "Box User (boxuser@box.com) has requested your signature on a document" + ), "parent_folder": { "id": "123456789", "etag": "0", "type": "folder", "sequence_id": "0", - "name": "My Sign Requests" + "name": "My Sign Requests", }, "auto_expire_days": "null", "source_files": [ @@ -30,8 +32,8 @@ def mock_sign_template_response(): "file_version": { "id": "123456", "type": "file_version", - "sha1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - } + "sha1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + }, } ], "are_email_settings_locked": "false", @@ -47,7 +49,7 @@ def mock_sign_template_response(): "role": "final_copy_reader", "is_in_person": "false", "order": 1, - "inputs": [] + "inputs": [], }, { "email": "", @@ -65,11 +67,11 @@ def mock_sign_template_response(): "is_required": True, "coordinates": { "x": 0.27038464059712, - "y": 0.10051756244533624 + "y": 0.10051756244533624, }, "dimensions": { "width": 0.23570031566618235, - "height": 0.04781003891921971 + "height": 0.04781003891921971, }, "date_value": None, "page_index": 0, @@ -78,38 +80,33 @@ def mock_sign_template_response(): "content_type": "signature", "dropdown_choices": None, "group_id": None, - "label": None + "label": None, } - ] - } + ], + }, ], "ready_sign_link": None, "custom_branding": None, "days_valid": 0, "additional_info": { "non_editable": [], - "required": { - "signers": [ - [ - "email" - ], - [ - "email" - ] - ] - } - } + "required": {"signers": [["email"], ["email"]]}, + }, } return mock_sign_template -def test_get_sign_template(test_sign_template, mock_box_session, mock_sign_template_response): +def test_get_sign_template( + test_sign_template, mock_box_session, mock_sign_template_response +): expected_url = f'{API.BASE_API_URL}/sign_templates/{test_sign_template.object_id}' mock_box_session.get.return_value.json.return_value = mock_sign_template_response sign_template = test_sign_template.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(sign_template, SignTemplate) diff --git a/test/unit/object/test_storage_policy.py b/test/unit/object/test_storage_policy.py index 009f52c5b..fb869916b 100644 --- a/test/unit/object/test_storage_policy.py +++ b/test/unit/object/test_storage_policy.py @@ -5,17 +5,23 @@ def test_get(test_storage_policy, mock_box_session): - expected_url = f'{API.BASE_API_URL}/storage_policies/{test_storage_policy.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/storage_policies/{test_storage_policy.object_id}' + ) mock_box_session.get.return_value.json.return_value = { 'type': test_storage_policy.object_type, 'id': test_storage_policy.object_id, } storage_policy = test_storage_policy.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(storage_policy, StoragePolicy) -def test_assign_with_same_assignment(test_storage_policy, test_storage_policy_assignment, mock_user, mock_box_session): +def test_assign_with_same_assignment( + test_storage_policy, test_storage_policy_assignment, mock_user, mock_box_session +): expected_url = f'{API.BASE_API_URL}/storage_policy_assignments' additional_params = { 'resolved_for_type': mock_user.object_type, @@ -44,7 +50,9 @@ def test_assign_with_same_assignment(test_storage_policy, test_storage_policy_as assert assignment.storage_policy['id'] == test_storage_policy.object_id -def test_assign_with_assigned_enterprise(test_storage_policy, test_storage_policy_assignment, mock_user, mock_box_session): +def test_assign_with_assigned_enterprise( + test_storage_policy, test_storage_policy_assignment, mock_user, mock_box_session +): expected_url = f'{API.BASE_API_URL}/storage_policy_assignments' additional_params = { 'resolved_for_type': mock_user.object_type, @@ -83,12 +91,14 @@ def test_assign_with_assigned_enterprise(test_storage_policy, test_storage_polic 'id': test_storage_policy_assignment.object_id, 'storage_policy': { 'type': test_storage_policy.object_type, - 'id': test_storage_policy.object_id - } + 'id': test_storage_policy.object_id, + }, } assignment = test_storage_policy.assign(mock_user) mock_box_session.get.assert_called_once_with(expected_url, params=additional_params) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert isinstance(assignment, StoragePolicyAssignment) assert assignment.type == test_storage_policy_assignment.object_type assert assignment.id == test_storage_policy_assignment.object_id @@ -96,7 +106,9 @@ def test_assign_with_assigned_enterprise(test_storage_policy, test_storage_polic assert assignment.storage_policy['id'] == test_storage_policy.object_id -def test_assign_with_update(test_storage_policy, test_storage_policy_assignment, mock_user, mock_box_session): +def test_assign_with_update( + test_storage_policy, test_storage_policy_assignment, mock_user, mock_box_session +): expected_url = f'{API.BASE_API_URL}/storage_policy_assignments' expected_put_url = f'{API.BASE_API_URL}/storage_policy_assignments/11111' additional_params = { @@ -130,14 +142,13 @@ def test_assign_with_update(test_storage_policy, test_storage_policy_assignment, mock_box_session.put.return_value.json.return_value = { 'type': test_storage_policy_assignment.object_type, 'id': test_storage_policy_assignment.object_id, - 'storage_policy': { - 'type': test_storage_policy.object_type, - 'id': '42' - } + 'storage_policy': {'type': test_storage_policy.object_type, 'id': '42'}, } assignment = test_storage_policy.assign(mock_user) mock_box_session.get.assert_called_once_with(expected_url, params=additional_params) - mock_box_session.put.assert_called_once_with(expected_put_url, data=json.dumps(expected_data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_put_url, data=json.dumps(expected_data), headers=None, params=None + ) assert isinstance(assignment, StoragePolicyAssignment) assert assignment.type == test_storage_policy_assignment.object_type assert assignment.id == test_storage_policy_assignment.object_id @@ -167,6 +178,8 @@ def test_create_assignment(test_storage_policy, mock_user, mock_box_session): } mock_box_session.post.return_value.json.return_value = mock_assignment assignment = test_storage_policy.create_assignment(mock_user) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_data)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_data) + ) assert assignment.id == mock_assignment['id'] assert assignment.storage_policy['id'] == mock_assignment['storage_policy']['id'] diff --git a/test/unit/object/test_storage_policy_assignment.py b/test/unit/object/test_storage_policy_assignment.py index 56ddcb57f..1f8cf84f0 100644 --- a/test/unit/object/test_storage_policy_assignment.py +++ b/test/unit/object/test_storage_policy_assignment.py @@ -22,7 +22,9 @@ def test_get(test_storage_policy_assignment, mock_box_session): 'id': test_storage_policy_assignment.object_id, } storage_policy_assignment = test_storage_policy_assignment.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(storage_policy_assignment, StoragePolicyAssignment) @@ -33,25 +35,33 @@ def test_update(test_storage_policy_assignment, mock_box_session): 'type': 'storage_policy_assignment', 'id': new_policy_id, } - storage_policy_assignment = test_storage_policy_assignment.update_info(data={ - 'storage_policy': { - 'type': 'storage_policy', - 'id': new_policy_id, + storage_policy_assignment = test_storage_policy_assignment.update_info( + data={ + 'storage_policy': { + 'type': 'storage_policy', + 'id': new_policy_id, + } } - }) + ) data = { 'storage_policy': { 'type': 'storage_policy', 'id': new_policy_id, } } - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(storage_policy_assignment, StoragePolicyAssignment) -def test_delete(test_storage_policy_assignment, delete_assignment_response, mock_box_session): +def test_delete( + test_storage_policy_assignment, delete_assignment_response, mock_box_session +): mock_box_session.delete.return_value = delete_assignment_response response = test_storage_policy_assignment.delete() expected_url = test_storage_policy_assignment.get_url() - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True diff --git a/test/unit/object/test_task.py b/test/unit/object/test_task.py index 6e5da14e4..592bc2286 100644 --- a/test/unit/object/test_task.py +++ b/test/unit/object/test_task.py @@ -29,7 +29,9 @@ def test_get(test_task, mock_box_session): 'message': message, } retrieved_task = test_task.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(retrieved_task, Task) assert retrieved_task.object_type == test_task.object_type assert retrieved_task.object_id == test_task.object_id @@ -50,7 +52,9 @@ def test_update(test_task, mock_box_session): 'message': new_message, } updated_task = test_task.update_info(data={'message': new_message}) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_body), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(expected_body), headers=None, params=None + ) assert isinstance(updated_task, Task) assert updated_task.message == new_message assert updated_task.object_type == test_task.object_type @@ -58,9 +62,9 @@ def test_update(test_task, mock_box_session): def test_delete_policy_return_the_correct_response( - test_task, - mock_box_session, - delete_task_response, + test_task, + mock_box_session, + delete_task_response, ): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value = delete_task_response @@ -68,7 +72,9 @@ def test_delete_policy_return_the_correct_response( # pylint:disable=protected-access expected_url = f'{API.BASE_API_URL}/tasks/{test_task.object_id}' # pylint:enable = protected-access - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True @@ -93,7 +99,9 @@ def test_assign(test_task, mock_user, mock_box_session): 'assigned_at': '2013-05-10T11:43:41-07:00', } new_task_assignment = test_task.assign(assignee=mock_user) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(new_task_assignment, TaskAssignment) assert new_task_assignment.object_type == 'task_assignment' assert new_task_assignment.object_id == '42' @@ -121,8 +129,12 @@ def test_assign_with_login(test_task, mock_box_session): }, 'assigned_at': '2013-05-10T11:43:41-07:00', } - new_task_assignment = test_task.assign_with_login(assignee_login='test_user@example.com') - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + new_task_assignment = test_task.assign_with_login( + assignee_login='test_user@example.com' + ) + mock_box_session.post.assert_called_once_with( + expected_url, data=json.dumps(expected_body) + ) assert isinstance(new_task_assignment, TaskAssignment) assert new_task_assignment.object_type == 'task_assignment' assert new_task_assignment.object_id == '42' @@ -135,14 +147,11 @@ def test_get_assignments(test_task, mock_box_session): mock_assignment = { 'type': 'task_assignment', 'id': '12345', - 'item': { - 'type': 'file', - 'id': '33333' - } + 'item': {'type': 'file', 'id': '33333'}, } mock_box_session.get.return_value.json.return_value = { 'limit': 100, - 'entries': [mock_assignment] + 'entries': [mock_assignment], } assignments = test_task.get_assignments() assignment = assignments.next() diff --git a/test/unit/object/test_task_assignment.py b/test/unit/object/test_task_assignment.py index 9473734c1..821d9e478 100644 --- a/test/unit/object/test_task_assignment.py +++ b/test/unit/object/test_task_assignment.py @@ -16,7 +16,9 @@ def delete_task_assignment_response(): def test_get_assignment(test_task_assignment, mock_box_session): - expected_url = f'{API.BASE_API_URL}/task_assignments/{test_task_assignment.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/task_assignments/{test_task_assignment.object_id}' + ) mock_box_session.get.return_value.json.return_value = { 'type': test_task_assignment.object_type, 'id': test_task_assignment.object_id, @@ -26,7 +28,9 @@ def test_get_assignment(test_task_assignment, mock_box_session): }, } retrieved_task = test_task_assignment.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(retrieved_task, TaskAssignment) assert retrieved_task.object_type == test_task_assignment.object_type assert retrieved_task.object_id == test_task_assignment.object_id @@ -35,24 +39,30 @@ def test_get_assignment(test_task_assignment, mock_box_session): def test_delete_policy_return_the_correct_response( - test_task_assignment, - mock_box_session, - delete_task_assignment_response, + test_task_assignment, + mock_box_session, + delete_task_assignment_response, ): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value = delete_task_assignment_response response = test_task_assignment.delete() # pylint:disable=protected-access - expected_url = f'{API.BASE_API_URL}/task_assignments/{test_task_assignment.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/task_assignments/{test_task_assignment.object_id}' + ) # pylint:enable = protected-access - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True def test_update(test_task_assignment, mock_box_session): new_message = 'New Message' resolution_state = ResolutionState.APPROVED - expected_url = f'{API.BASE_API_URL}/task_assignments/{test_task_assignment.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/task_assignments/{test_task_assignment.object_id}' + ) mock_box_session.put.return_value.json.return_value = { 'type': 'task_assignment', 'id': test_task_assignment.object_id, @@ -63,8 +73,12 @@ def test_update(test_task_assignment, mock_box_session): 'message': new_message, 'resolution_state': resolution_state, } - updated_task_assignment = test_task_assignment.update_info(data={'message': new_message, 'resolution_state': resolution_state}) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_body), headers=None, params=None) + updated_task_assignment = test_task_assignment.update_info( + data={'message': new_message, 'resolution_state': resolution_state} + ) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(expected_body), headers=None, params=None + ) assert isinstance(updated_task_assignment, TaskAssignment) assert updated_task_assignment.message == new_message assert updated_task_assignment.object_type == test_task_assignment.object_type diff --git a/test/unit/object/test_terms_of_service.py b/test/unit/object/test_terms_of_service.py index 928a1eeee..39cb1f7ef 100644 --- a/test/unit/object/test_terms_of_service.py +++ b/test/unit/object/test_terms_of_service.py @@ -8,14 +8,18 @@ def test_get(test_terms_of_service, mock_box_session): created_at = '2016-05-18T17:38:03-07:00' - expected_url = f'{API.BASE_API_URL}/terms_of_services/{test_terms_of_service.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/terms_of_services/{test_terms_of_service.object_id}' + ) mock_box_session.get.return_value.json.return_value = { 'type': 'terms_of_service', 'id': test_terms_of_service.object_id, 'created_at': created_at, } terms_of_service = test_terms_of_service.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(terms_of_service, TermsOfService) assert terms_of_service.created_at == created_at assert terms_of_service.id == test_terms_of_service.object_id @@ -24,7 +28,9 @@ def test_get(test_terms_of_service, mock_box_session): def test_update(test_terms_of_service, mock_box_session): new_text = 'This is new text' - expected_url = f'{API.BASE_API_URL}/terms_of_services/{test_terms_of_service.object_id}' + expected_url = ( + f'{API.BASE_API_URL}/terms_of_services/{test_terms_of_service.object_id}' + ) mock_box_session.put.return_value.json.return_value = { 'type': 'terms_of_service', 'id': test_terms_of_service.object_id, @@ -34,28 +40,37 @@ def test_update(test_terms_of_service, mock_box_session): 'text': new_text, } updated_terms_of_service = test_terms_of_service.update_info(data=data) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(updated_terms_of_service, TermsOfService) assert updated_terms_of_service.text == new_text assert updated_terms_of_service.id == test_terms_of_service.object_id assert updated_terms_of_service.type == test_terms_of_service.object_type -def test_accept_terms_of_service(test_terms_of_service, test_terms_of_service_user_status, mock_user, mock_box_session): +def test_accept_terms_of_service( + test_terms_of_service, + test_terms_of_service_user_status, + mock_user, + mock_box_session, +): # pylint:disable=redefined-outer-name expected_url = f"{API.BASE_API_URL}/terms_of_service_user_statuses" created_at = '2016-05-18T17:38:03-07:00' - value = json.dumps({ - 'tos': { - 'type': 'terms_of_service', - 'id': test_terms_of_service.object_id, - }, - 'is_accepted': True, - 'user': { - 'type': mock_user.object_type, - 'id': mock_user.object_id, - }, - }) + value = json.dumps( + { + 'tos': { + 'type': 'terms_of_service', + 'id': test_terms_of_service.object_id, + }, + 'is_accepted': True, + 'user': { + 'type': mock_user.object_type, + 'id': mock_user.object_id, + }, + } + ) mock_box_session.post.return_value.json.return_value = { 'type': 'terms_of_service_user_status', 'id': test_terms_of_service_user_status.object_id, @@ -64,26 +79,39 @@ def test_accept_terms_of_service(test_terms_of_service, test_terms_of_service_us new_terms_of_service_user_status = test_terms_of_service.accept(mock_user) mock_box_session.post.assert_called_once_with(expected_url, data=value) assert isinstance(new_terms_of_service_user_status, TermsOfServiceUserStatus) - assert new_terms_of_service_user_status.id == test_terms_of_service_user_status.object_id - assert new_terms_of_service_user_status.type == test_terms_of_service_user_status.object_type + assert ( + new_terms_of_service_user_status.id + == test_terms_of_service_user_status.object_id + ) + assert ( + new_terms_of_service_user_status.type + == test_terms_of_service_user_status.object_type + ) assert new_terms_of_service_user_status.created_at == created_at -def test_reject_terms_of_service(test_terms_of_service, test_terms_of_service_user_status, mock_user, mock_box_session): +def test_reject_terms_of_service( + test_terms_of_service, + test_terms_of_service_user_status, + mock_user, + mock_box_session, +): # pylint:disable=redefined-outer-name expected_url = f"{API.BASE_API_URL}/terms_of_service_user_statuses" created_at = '2016-05-18T17:38:03-07:00' - value = json.dumps({ - 'tos': { - 'type': 'terms_of_service', - 'id': test_terms_of_service.object_id, - }, - 'is_accepted': False, - 'user': { - 'type': mock_user.object_type, - 'id': mock_user.object_id, - }, - }) + value = json.dumps( + { + 'tos': { + 'type': 'terms_of_service', + 'id': test_terms_of_service.object_id, + }, + 'is_accepted': False, + 'user': { + 'type': mock_user.object_type, + 'id': mock_user.object_id, + }, + } + ) mock_box_session.post.return_value.json.return_value = { 'type': 'terms_of_service_user_status', 'id': test_terms_of_service_user_status.object_id, @@ -92,12 +120,23 @@ def test_reject_terms_of_service(test_terms_of_service, test_terms_of_service_us new_terms_of_service_user_status = test_terms_of_service.reject(mock_user) mock_box_session.post.assert_called_once_with(expected_url, data=value) assert isinstance(new_terms_of_service_user_status, TermsOfServiceUserStatus) - assert new_terms_of_service_user_status.id == test_terms_of_service_user_status.object_id - assert new_terms_of_service_user_status.type == test_terms_of_service_user_status.object_type + assert ( + new_terms_of_service_user_status.id + == test_terms_of_service_user_status.object_id + ) + assert ( + new_terms_of_service_user_status.type + == test_terms_of_service_user_status.object_type + ) assert new_terms_of_service_user_status.created_at == created_at -def test_get_user_status(test_terms_of_service, mock_user, test_terms_of_service_user_status, mock_box_session): +def test_get_user_status( + test_terms_of_service, + mock_user, + test_terms_of_service_user_status, + mock_box_session, +): expected_url = f"{API.BASE_API_URL}/terms_of_service_user_statuses" created_at = '2016-05-18T17:38:03-07:00' expected_params = { @@ -113,11 +152,16 @@ def test_get_user_status(test_terms_of_service, mock_user, test_terms_of_service 'total_count': 2, 'entries': [mock_user_status], } - new_terms_of_service_user_status = test_terms_of_service.get_user_status(user=mock_user) + new_terms_of_service_user_status = test_terms_of_service.get_user_status( + user=mock_user + ) mock_box_session.get.assert_called_once_with(expected_url, params=expected_params) assert isinstance(new_terms_of_service_user_status, TermsOfServiceUserStatus) assert new_terms_of_service_user_status.type == 'terms_of_service_user_status' - assert new_terms_of_service_user_status.id == test_terms_of_service_user_status.object_id + assert ( + new_terms_of_service_user_status.id + == test_terms_of_service_user_status.object_id + ) assert new_terms_of_service_user_status.created_at == created_at @@ -126,28 +170,32 @@ def test_get_empty_user_status(test_terms_of_service, mock_user, mock_box_sessio 'total_count': 1, 'entries': [], } - new_terms_of_service_user_status = test_terms_of_service.get_user_status(user=mock_user) + new_terms_of_service_user_status = test_terms_of_service.get_user_status( + user=mock_user + ) assert new_terms_of_service_user_status is None def test_set_user_status(test_terms_of_service, mock_user, mock_box_session): expected_post_url = f"{API.BASE_API_URL}/terms_of_service_user_statuses" expected_put_url = f"{API.BASE_API_URL}/terms_of_service_user_statuses/{test_terms_of_service.object_id}" - post_value = json.dumps({ - 'tos': { - 'type': test_terms_of_service.object_type, - 'id': test_terms_of_service.object_id, - }, - 'is_accepted': True, - 'user': { - 'type': mock_user.object_type, - 'id': mock_user.object_id, - }, - }) - put_value = json.dumps({ - 'is_accepted': True - }) - mock_box_session.post.side_effect = [BoxAPIException(status=409, message="Conflict")] + post_value = json.dumps( + { + 'tos': { + 'type': test_terms_of_service.object_type, + 'id': test_terms_of_service.object_id, + }, + 'is_accepted': True, + 'user': { + 'type': mock_user.object_type, + 'id': mock_user.object_id, + }, + } + ) + put_value = json.dumps({'is_accepted': True}) + mock_box_session.post.side_effect = [ + BoxAPIException(status=409, message="Conflict") + ] mock_box_session.get.return_value.json.return_value = { 'entries': [ { @@ -165,12 +213,19 @@ def test_set_user_status(test_terms_of_service, mock_user, mock_box_session): }, 'is_accepted': True, } - new_terms_of_service_user_status = test_terms_of_service.set_user_status(True, mock_user) + new_terms_of_service_user_status = test_terms_of_service.set_user_status( + True, mock_user + ) mock_box_session.post.assert_called_once_with(expected_post_url, data=post_value) - mock_box_session.put.assert_called_once_with(expected_put_url, data=put_value, headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_put_url, data=put_value, headers=None, params=None + ) assert isinstance(new_terms_of_service_user_status, TermsOfServiceUserStatus) assert new_terms_of_service_user_status.type == 'terms_of_service_user_status' assert new_terms_of_service_user_status.id == '12345' - assert new_terms_of_service_user_status.tos['type'] == test_terms_of_service.object_type + assert ( + new_terms_of_service_user_status.tos['type'] + == test_terms_of_service.object_type + ) assert new_terms_of_service_user_status.tos['id'] == test_terms_of_service.object_id assert new_terms_of_service_user_status.is_accepted is True diff --git a/test/unit/object/test_terms_of_service_user_status.py b/test/unit/object/test_terms_of_service_user_status.py index 0c4362dd2..7558c2b3a 100644 --- a/test/unit/object/test_terms_of_service_user_status.py +++ b/test/unit/object/test_terms_of_service_user_status.py @@ -10,10 +10,12 @@ def test_get(test_terms_of_service_user_status, mock_box_session): mock_box_session.get.return_value.json.return_value = { 'type': 'terms_of_service_user_status', 'id': test_terms_of_service_user_status.object_id, - 'created_at': created_at + 'created_at': created_at, } terms_of_service = test_terms_of_service_user_status.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(terms_of_service, TermsOfServiceUserStatus) assert terms_of_service.type == test_terms_of_service_user_status.object_type assert terms_of_service.id == test_terms_of_service_user_status.object_id @@ -25,14 +27,21 @@ def test_accept(test_terms_of_service_user_status, mock_box_session): mock_box_session.put.return_value.json.return_value = { 'type': test_terms_of_service_user_status.object_type, 'id': test_terms_of_service_user_status.object_id, - 'is_accepted': True + 'is_accepted': True, } data = {'is_accepted': True} terms_of_service_user_status = test_terms_of_service_user_status.accept() - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(terms_of_service_user_status, TermsOfServiceUserStatus) - assert terms_of_service_user_status.type == test_terms_of_service_user_status.object_type - assert terms_of_service_user_status.id == test_terms_of_service_user_status.object_id + assert ( + terms_of_service_user_status.type + == test_terms_of_service_user_status.object_type + ) + assert ( + terms_of_service_user_status.id == test_terms_of_service_user_status.object_id + ) assert terms_of_service_user_status.is_accepted is True @@ -41,12 +50,19 @@ def test_reject(test_terms_of_service_user_status, mock_box_session): mock_box_session.put.return_value.json.return_value = { 'type': 'terms_of_service_user_status', 'id': test_terms_of_service_user_status.object_id, - 'is_accepted': False + 'is_accepted': False, } data = {'is_accepted': False} terms_of_service_user_status = test_terms_of_service_user_status.reject() - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(terms_of_service_user_status, TermsOfServiceUserStatus) - assert terms_of_service_user_status.type == test_terms_of_service_user_status.object_type - assert terms_of_service_user_status.id == test_terms_of_service_user_status.object_id + assert ( + terms_of_service_user_status.type + == test_terms_of_service_user_status.object_type + ) + assert ( + terms_of_service_user_status.id == test_terms_of_service_user_status.object_id + ) assert terms_of_service_user_status.is_accepted is False diff --git a/test/unit/object/test_trash.py b/test/unit/object/test_trash.py index 83ee7d979..ebfd449fb 100644 --- a/test/unit/object/test_trash.py +++ b/test/unit/object/test_trash.py @@ -12,13 +12,13 @@ def test_trash(mock_box_session): @pytest.fixture(params=('file', 'folder', 'web_link')) def test_item_and_response( - test_file, - mock_file_response, - test_folder, - mock_folder_response, - test_web_link, - mock_web_link_response, - request, + test_file, + mock_file_response, + test_folder, + mock_folder_response, + test_web_link, + mock_web_link_response, + request, ): if request.param == 'file': return test_file, mock_file_response @@ -30,7 +30,9 @@ def test_item_and_response( def test_get_from_trash(test_item_and_response, test_trash, mock_box_session): # pylint:disable=redefined-outer-name, protected-access test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type + "s"}/{test_item.object_id}/trash' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type + "s"}/{test_item.object_id}/trash' + ) mock_box_session.get.return_value.json.return_value = { 'type': test_item.object_type, 'id': test_item.object_id, @@ -42,9 +44,11 @@ def test_get_from_trash(test_item_and_response, test_trash, mock_box_session): }, } trashed_item_info = test_trash.get_item( - item=test_item, fields=['created_at', 'modified_at']) + item=test_item, fields=['created_at', 'modified_at'] + ) mock_box_session.get.assert_called_once_with( - expected_url, params={'fields': 'created_at,modified_at'}) + expected_url, params={'fields': 'created_at,modified_at'} + ) assert isinstance(trashed_item_info, test_item.__class__) assert trashed_item_info.object_type == test_item.object_type assert trashed_item_info.object_id == test_item.object_id @@ -54,17 +58,23 @@ def test_get_from_trash(test_item_and_response, test_trash, mock_box_session): assert trashed_item_info.created_by['id'] == '11111' -def test_restore_from_trash(test_item_and_response, test_trash, test_folder, mock_box_session): +def test_restore_from_trash( + test_item_and_response, test_trash, test_folder, mock_box_session +): # pylint:disable=redefined-outer-name, protected-access test_item, _ = test_item_and_response new_name = 'New Name' - expected_url = f'{API.BASE_API_URL}/{test_item.object_type + "s"}/{test_item.object_id}' - value = json.dumps({ - 'name': new_name, - 'parent': { - 'id': test_folder.object_id, - }, - }) + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type + "s"}/{test_item.object_id}' + ) + value = json.dumps( + { + 'name': new_name, + 'parent': { + 'id': test_folder.object_id, + }, + } + ) mock_box_session.post.return_value.json.return_value = { 'type': test_item.object_type, 'id': test_item.object_id, @@ -75,10 +85,12 @@ def test_restore_from_trash(test_item_and_response, test_trash, test_folder, moc 'id': '11111', }, } - restored_item = test_trash.restore_item(test_item, new_name, test_folder, [ - 'created_at', 'modified_at']) - mock_box_session.post.assert_called_once_with(expected_url, data=value, params={ - 'fields': 'created_at,modified_at'}) + restored_item = test_trash.restore_item( + test_item, new_name, test_folder, ['created_at', 'modified_at'] + ) + mock_box_session.post.assert_called_once_with( + expected_url, data=value, params={'fields': 'created_at,modified_at'} + ) assert isinstance(restored_item, test_item.__class__) assert restored_item.object_type == test_item.object_type assert restored_item.object_id == test_item.object_id @@ -91,11 +103,14 @@ def test_restore_from_trash(test_item_and_response, test_trash, test_folder, moc def test_permanently_delete(test_item_and_response, test_trash, mock_box_session): # pylint:disable=redefined-outer-name, protected-access test_item, _ = test_item_and_response - expected_url = f'{API.BASE_API_URL}/{test_item.object_type + "s"}/{test_item.object_id}/trash' + expected_url = ( + f'{API.BASE_API_URL}/{test_item.object_type + "s"}/{test_item.object_id}/trash' + ) mock_box_session.delete.return_value.ok = True info = test_trash.permanently_delete_item(test_item) mock_box_session.delete.assert_called_once_with( - expected_url, expect_json_response=False) + expected_url, expect_json_response=False + ) assert info is True @@ -106,65 +121,75 @@ def test_get_trashed_items(test_item_and_response, test_trash, mock_box_session) mock_trash = { 'type': test_item.object_type, 'id': test_item.object_id, - 'name': 'Test Trashed Item' + 'name': 'Test Trashed Item', } mock_box_session.get.return_value.json.return_value = { 'total_count': 5, 'offset': 0, 'limit': 100, - 'entries': [mock_trash] + 'entries': [mock_trash], } trashed_items = test_trash.get_items(fields=['name']) trashed_item = trashed_items.next() mock_box_session.get.assert_called_once_with( - expected_url, params={'fields': 'name', 'offset': None}) + expected_url, params={'fields': 'name', 'offset': None} + ) assert isinstance(trashed_item, test_item.__class__) assert trashed_item.type == mock_trash['type'] assert trashed_item.id == mock_trash['id'] assert trashed_item.name == item_name -def test_get_trashed_items_with_sort(test_item_and_response, test_trash, mock_box_session): +def test_get_trashed_items_with_sort( + test_item_and_response, test_trash, mock_box_session +): test_item, _ = test_item_and_response expected_url = f'{API.BASE_API_URL}/folders/trash/items' mock_trash = { 'type': test_item.object_type, 'id': test_item.object_id, - 'name': 'Test Trashed Item' + 'name': 'Test Trashed Item', } mock_box_session.get.return_value.json.return_value = { 'total_count': 5, 'offset': 0, 'limit': 100, - 'entries': [mock_trash] + 'entries': [mock_trash], } trashed_items = test_trash.get_items(fields=['name'], sort='name', direction='ASC') trashed_item = trashed_items.next() mock_box_session.get.assert_called_once_with( - expected_url, params={'direction': 'ASC', 'sort': 'name', 'offset': None, 'fields': 'name'}) + expected_url, + params={'direction': 'ASC', 'sort': 'name', 'offset': None, 'fields': 'name'}, + ) assert isinstance(trashed_item, test_item.__class__) -def test_get_trashed_items_with_marker(test_item_and_response, test_trash, mock_box_session): +def test_get_trashed_items_with_marker( + test_item_and_response, test_trash, mock_box_session +): test_item, _ = test_item_and_response item_name = 'Test Trashed Item' expected_url = f'{API.BASE_API_URL}/folders/trash/items' mock_trash = { 'type': test_item.object_type, 'id': test_item.object_id, - 'name': 'Test Trashed Item' + 'name': 'Test Trashed Item', } mock_box_session.get.return_value.json.return_value = { 'limit': 100, 'next_marker': 2345, - 'entries': [mock_trash] + 'entries': [mock_trash], } trashed_items = test_trash.get_items( - fields=['name'], limit=100, marker=1234, use_marker=True) + fields=['name'], limit=100, marker=1234, use_marker=True + ) trashed_item = trashed_items.next() - mock_box_session.get.assert_called_once_with(expected_url, params={ - 'limit': 100, 'usemarker': True, 'marker': 1234, 'fields': 'name'}) + mock_box_session.get.assert_called_once_with( + expected_url, + params={'limit': 100, 'usemarker': True, 'marker': 1234, 'fields': 'name'}, + ) assert isinstance(trashed_item, test_item.__class__) assert trashed_item.type == mock_trash['type'] assert trashed_item.id == mock_trash['id'] diff --git a/test/unit/object/test_upload_session.py b/test/unit/object/test_upload_session.py index ff66e90c1..aff079140 100644 --- a/test/unit/object/test_upload_session.py +++ b/test/unit/object/test_upload_session.py @@ -12,15 +12,26 @@ from boxsdk.object.file import File from boxsdk.object.upload_session import UploadSession - UPLOAD_SESSION_ID = 'F971964745A5CD0C001BBE4E58196BFD' SESSION_ENDPOINTS = { - "abort": f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}", - "commit": f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}/commit", - "list_parts": f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}/parts", - "log_event": f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}/log", - "status": f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}", - "upload_part": f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}" + "abort": ( + f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}" + ), + "commit": ( + f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}/commit" + ), + "list_parts": ( + f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}/parts" + ), + "log_event": ( + f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}/log" + ), + "status": ( + f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}" + ), + "upload_part": ( + f"https://changed.box.com/api/2.0/files/upload_sessions/{UPLOAD_SESSION_ID}" + ), } @@ -35,7 +46,7 @@ def upload_session_using_upload_session_urls(mock_box_session): mock_box_session, UPLOAD_SESSION_ID, upload_session_response_object, - use_upload_session_urls=True + use_upload_session_urls=True, ) @@ -50,17 +61,23 @@ def upload_session_not_using_upload_session_urls(mock_box_session): mock_box_session, UPLOAD_SESSION_ID, upload_session_response_object, - use_upload_session_urls=False + use_upload_session_urls=False, ) @pytest.mark.parametrize( 'test_upload_session, expected_url', [ - (lazy_fixture('upload_session_using_upload_session_urls'), SESSION_ENDPOINTS['list_parts']), - (lazy_fixture('upload_session_not_using_upload_session_urls'), - f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/parts') - ]) + ( + lazy_fixture('upload_session_using_upload_session_urls'), + SESSION_ENDPOINTS['list_parts'], + ), + ( + lazy_fixture('upload_session_not_using_upload_session_urls'), + f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/parts', + ), + ], +) def test_get_parts(mock_box_session, test_upload_session, expected_url): mock_entry = { 'part_id': '8F0966B1', @@ -86,24 +103,38 @@ def test_get_parts(mock_box_session, test_upload_session, expected_url): @pytest.mark.parametrize( 'test_upload_session, expected_url', [ - (lazy_fixture('upload_session_using_upload_session_urls'), SESSION_ENDPOINTS['abort']), - (lazy_fixture('upload_session_not_using_upload_session_urls'), - f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}') - ]) + ( + lazy_fixture('upload_session_using_upload_session_urls'), + SESSION_ENDPOINTS['abort'], + ), + ( + lazy_fixture('upload_session_not_using_upload_session_urls'), + f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}', + ), + ], +) def test_abort(mock_box_session, test_upload_session, expected_url): mock_box_session.delete.return_value.ok = True result = test_upload_session.abort() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False + ) assert result is True @pytest.mark.parametrize( 'test_upload_session, expected_url', [ - (lazy_fixture('upload_session_using_upload_session_urls'), SESSION_ENDPOINTS['upload_part']), - (lazy_fixture('upload_session_not_using_upload_session_urls'), - f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}') - ]) + ( + lazy_fixture('upload_session_using_upload_session_urls'), + SESSION_ENDPOINTS['upload_part'], + ), + ( + lazy_fixture('upload_session_not_using_upload_session_urls'), + f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}', + ), + ], +) def test_upload_part_bytes(mock_box_session, test_upload_session, expected_url): part_bytes = b'abcdefgh' offset = 32 @@ -124,7 +155,9 @@ def test_upload_part_bytes(mock_box_session, test_upload_session, expected_url): } part = test_upload_session.upload_part_bytes(part_bytes, offset, total_size) - mock_box_session.put.assert_called_once_with(expected_url, data=part_bytes, headers=expected_headers) + mock_box_session.put.assert_called_once_with( + expected_url, data=part_bytes, headers=expected_headers + ) assert isinstance(part, dict) assert part['sha1'] == expected_sha1 assert part['size'] == 8 @@ -134,10 +167,16 @@ def test_upload_part_bytes(mock_box_session, test_upload_session, expected_url): @pytest.mark.parametrize( 'test_upload_session, expected_url', [ - (lazy_fixture('upload_session_using_upload_session_urls'), SESSION_ENDPOINTS['commit']), - (lazy_fixture('upload_session_not_using_upload_session_urls'), - f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit') - ]) + ( + lazy_fixture('upload_session_using_upload_session_urls'), + SESSION_ENDPOINTS['commit'], + ), + ( + lazy_fixture('upload_session_not_using_upload_session_urls'), + f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit', + ), + ], +) def test_commit(mock_box_session, test_upload_session, expected_url): sha1 = hashlib.sha1() sha1.update(b'fake_file_data') @@ -178,7 +217,10 @@ def test_commit(mock_box_session, test_upload_session, expected_url): ], } created_file = test_upload_session.commit( - content_sha1=sha1.digest(), parts=parts, file_attributes=file_attributes, etag=file_etag + content_sha1=sha1.digest(), + parts=parts, + file_attributes=file_attributes, + etag=file_etag, ) mock_box_session.post.assert_called_once_with( expected_url, data=json.dumps(expected_data), headers=expected_headers @@ -195,15 +237,18 @@ def test_commit(mock_box_session, test_upload_session, expected_url): ( lazy_fixture('upload_session_using_upload_session_urls'), SESSION_ENDPOINTS['list_parts'], - SESSION_ENDPOINTS['commit'] + SESSION_ENDPOINTS['commit'], ), ( lazy_fixture('upload_session_not_using_upload_session_urls'), f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/parts', - f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit' - ) - ]) -def test_commit_with_missing_params(mock_box_session, test_upload_session, expected_get_url, expected_commit_url): + f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit', + ), + ], +) +def test_commit_with_missing_params( + mock_box_session, test_upload_session, expected_get_url, expected_commit_url +): sha1 = hashlib.sha1() sha1.update(b'fake_file_data') file_id = '12345' @@ -244,7 +289,9 @@ def test_commit_with_missing_params(mock_box_session, test_upload_session, expec ], } created_file = test_upload_session.commit(content_sha1=sha1.digest()) - mock_box_session.get.assert_called_once_with(expected_get_url, params={'offset': None}) + mock_box_session.get.assert_called_once_with( + expected_get_url, params={'offset': None} + ) mock_box_session.post.assert_called_once_with( expected_commit_url, data=json.dumps(expected_data), headers=expected_headers ) @@ -256,11 +303,19 @@ def test_commit_with_missing_params(mock_box_session, test_upload_session, expec @pytest.mark.parametrize( 'test_upload_session, expected_url', [ - (lazy_fixture('upload_session_using_upload_session_urls'), SESSION_ENDPOINTS['commit']), - (lazy_fixture('upload_session_not_using_upload_session_urls'), - f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit') - ]) -def test_commit_returns_none_when_202_is_returned(mock_box_session, test_upload_session, expected_url): + ( + lazy_fixture('upload_session_using_upload_session_urls'), + SESSION_ENDPOINTS['commit'], + ), + ( + lazy_fixture('upload_session_not_using_upload_session_urls'), + f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit', + ), + ], +) +def test_commit_returns_none_when_202_is_returned( + mock_box_session, test_upload_session, expected_url +): sha1 = hashlib.sha1() sha1.update(b'fake_file_data') file_etag = '7' @@ -291,7 +346,10 @@ def test_commit_returns_none_when_202_is_returned(mock_box_session, test_upload_ mock_box_session.post.side_effect = BoxAPIException(status=202) created_file = test_upload_session.commit( - content_sha1=sha1.digest(), parts=parts, file_attributes=file_attributes, etag=file_etag + content_sha1=sha1.digest(), + parts=parts, + file_attributes=file_attributes, + etag=file_etag, ) mock_box_session.post.assert_called_once_with( @@ -305,12 +363,15 @@ def test_commit_returns_none_when_202_is_returned(mock_box_session, test_upload_ [ lazy_fixture('upload_session_using_upload_session_urls'), lazy_fixture('upload_session_not_using_upload_session_urls'), - ]) + ], +) def test_get_chunked_uploader_for_stream(test_upload_session): file_size = 197520 part_bytes = b'abcdefgh' stream = io.BytesIO(part_bytes) - chunked_uploader = test_upload_session.get_chunked_uploader_for_stream(stream, file_size) + chunked_uploader = test_upload_session.get_chunked_uploader_for_stream( + stream, file_size + ) assert isinstance(chunked_uploader, ChunkedUploader) @@ -319,8 +380,11 @@ def test_get_chunked_uploader_for_stream(test_upload_session): [ lazy_fixture('upload_session_using_upload_session_urls'), lazy_fixture('upload_session_not_using_upload_session_urls'), - ]) -def test_get_chunked_uploader(mock_content_response, mock_file_path, test_upload_session): + ], +) +def test_get_chunked_uploader( + mock_content_response, mock_file_path, test_upload_session +): mock_file_stream = io.BytesIO(mock_content_response.content) file_size = 197520 with patch('os.stat') as stat: @@ -330,19 +394,28 @@ def test_get_chunked_uploader(mock_content_response, mock_file_path, test_upload assert isinstance(chunked_uploader, ChunkedUploader) -def test_get_url_do_not_use_session_urls_if_base_url_was_changed(upload_session_using_upload_session_urls): +def test_get_url_do_not_use_session_urls_if_base_url_was_changed( + upload_session_using_upload_session_urls, +): old_base_upload_url = API.UPLOAD_URL new_base_upload_url = 'https://new-upload.box.com/api/2.0' API.UPLOAD_URL = new_base_upload_url try: - url = upload_session_using_upload_session_urls.get_url('commit', url_key='commit') - assert url == f'{new_base_upload_url}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit' + url = upload_session_using_upload_session_urls.get_url( + 'commit', url_key='commit' + ) + assert ( + url + == f'{new_base_upload_url}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit' + ) assert url != SESSION_ENDPOINTS['commit'] finally: API.UPLOAD_URL = old_base_upload_url -def test_get_url_uses_session_urls_if_base_url_was_not_changed(upload_session_using_upload_session_urls): +def test_get_url_uses_session_urls_if_base_url_was_not_changed( + upload_session_using_upload_session_urls, +): url = upload_session_using_upload_session_urls.get_url('commit', url_key='commit') assert url != f'{API.UPLOAD_URL}/files/upload_sessions/{UPLOAD_SESSION_ID}/commit' assert url == SESSION_ENDPOINTS['commit'] diff --git a/test/unit/object/test_user.py b/test/unit/object/test_user.py index 9043c5779..2dfdab281 100644 --- a/test/unit/object/test_user.py +++ b/test/unit/object/test_user.py @@ -15,7 +15,9 @@ def test_user_url(mock_user): assert mock_user.get_url() == f'{API.BASE_API_URL}/users/{mock_user.object_id}' -def test_get_storage_policy_assignments(test_storage_policy_assignment, mock_user, mock_box_session): +def test_get_storage_policy_assignments( + test_storage_policy_assignment, mock_user, mock_box_session +): expected_url = mock_box_session.get_url('storage_policy_assignments') mock_assignment = { 'type': test_storage_policy_assignment.object_type, @@ -47,10 +49,18 @@ def memberships_response(): mock_network_response = Mock(DefaultNetworkResponse) mock_network_response.json.return_value = { 'entries': [ - {'type': 'group_membership', 'id': 101, 'user': {'type': 'user', 'id': 100}, - 'group': {'type': 'group', 'id': 300}}, - {'type': 'group_membership', 'id': 202, 'user': {'type': 'user', 'id': 200}, - 'group': {'type': 'group', 'id': 400}} + { + 'type': 'group_membership', + 'id': 101, + 'user': {'type': 'user', 'id': 100}, + 'group': {'type': 'group', 'id': 300}, + }, + { + 'type': 'group_membership', + 'id': 202, + 'user': {'type': 'user', 'id': 200}, + 'group': {'type': 'group', 'id': 400}, + }, ], 'limit': 2, 'total_count': 2, @@ -81,7 +91,9 @@ def test_update(mock_user, mock_box_session): } mock_box_session.put.return_value.json.return_value = user new_user = mock_user.update_info(data=data) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert new_user.id == user['id'] assert new_user.type == user['type'] assert new_user.name == user['name'] @@ -127,13 +139,17 @@ def test_get_email_aliases(mock_user, mock_box_session): assert alias.email == alias_json['email'] -def test_add_email_alias_returns_the_correct_email_alias_object(mock_user, mock_box_session): +def test_add_email_alias_returns_the_correct_email_alias_object( + mock_user, mock_box_session +): # pylint:disable=redefined-outer-name test_email_alias = 'test@example.com' expected_url = f'{API.BASE_API_URL}/users/{mock_user.object_id}/email_aliases' - expected_body = json.dumps({ - 'email': test_email_alias, - }) + expected_body = json.dumps( + { + 'email': test_email_alias, + } + ) mock_box_session.post.return_value.json.return_value = { 'type': 'email_alias', 'id': '1234', @@ -142,7 +158,9 @@ def test_add_email_alias_returns_the_correct_email_alias_object(mock_user, mock_ new_email_alias = mock_user.add_email_alias(test_email_alias) mock_box_session.post.assert_called_once_with(expected_url, data=expected_body) assert isinstance(new_email_alias, EmailAlias) - assert new_email_alias._session == mock_box_session # pylint: disable=protected-access + assert ( + new_email_alias._session == mock_box_session + ) # pylint: disable=protected-access assert new_email_alias.object_id == '1234' @@ -152,25 +170,30 @@ def test_remove_email_alias(mock_user, mock_box_session, test_email_alias): result = mock_user.remove_email_alias(test_email_alias) - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False + ) assert result is True -@pytest.mark.parametrize('notify,fields,expected_params', [ - (None, None, {}), - (True, None, {'notify': True}), - (False, None, {'notify': False}), - (None, ['type', 'id', 'name'], {'fields': 'type,id,name'}), - (False, ['type', 'id'], {'notify': False, 'fields': 'type,id'}), -]) +@pytest.mark.parametrize( + 'notify,fields,expected_params', + [ + (None, None, {}), + (True, None, {'notify': True}), + (False, None, {'notify': False}), + (None, ['type', 'id', 'name'], {'fields': 'type,id,name'}), + (False, ['type', 'id'], {'notify': False, 'fields': 'type,id'}), + ], +) def test_transfer_content(mock_user, mock_box_session, notify, fields, expected_params): # pylint:disable=redefined-outer-name expected_url = f"{API.BASE_API_URL}/users/{mock_user.object_id}/folders/0" - expected_body = json.dumps({ - 'owned_by': { - 'id': mock_user.object_id - }, - }) + expected_body = json.dumps( + { + 'owned_by': {'id': mock_user.object_id}, + } + ) move_items_response = { 'type': 'folder', 'id': '12345', @@ -178,7 +201,9 @@ def test_transfer_content(mock_user, mock_box_session, notify, fields, expected_ } mock_box_session.put.return_value.json.return_value = move_items_response moved_item = mock_user.transfer_content(mock_user, notify=notify, fields=fields) - mock_box_session.put.assert_called_once_with(expected_url, data=expected_body, params=expected_params) + mock_box_session.put.assert_called_once_with( + expected_url, data=expected_body, params=expected_params + ) assert isinstance(moved_item, Folder) assert moved_item.id == move_items_response['id'] assert moved_item.name == move_items_response['name'] @@ -186,9 +211,9 @@ def test_transfer_content(mock_user, mock_box_session, notify, fields, expected_ def test_get_group_memberships( - mock_user, - mock_box_session, - memberships_response, + mock_user, + mock_box_session, + memberships_response, ): # pylint:disable=redefined-outer-name expected_url = f'{API.BASE_API_URL}/users/{mock_user.object_id}/memberships' @@ -215,7 +240,9 @@ def test_get_user_avatar(mock_user, mock_box_session, mock_content_response): @pytest.mark.parametrize('image_extension', ['jpg', 'jpeg', 'png']) def test_upload_avatar(mock_user, mock_box_session, mock_image_path, image_extension): expected_url = mock_user.get_url('avatar') - upload_avatar_response = {'pic_urls': {'large': 'url1', 'preview': 'url2', 'small': 'url3'}} + upload_avatar_response = { + 'pic_urls': {'large': 'url1', 'preview': 'url2', 'small': 'url3'} + } mock_box_session.post.return_value.json.return_value = upload_avatar_response with patch('boxsdk.object.user.open', mock_open()) as mock_image: @@ -224,23 +251,39 @@ def test_upload_avatar(mock_user, mock_box_session, mock_image_path, image_exten assert avatar_urls == upload_avatar_response['pic_urls'] mock_box_session.post.assert_called_once_with( expected_url, - files={'pic': (f'avatar.{image_extension}', mock_image.return_value, f'image/{image_extension}')} + files={ + 'pic': ( + f'avatar.{image_extension}', + mock_image.return_value, + f'image/{image_extension}', + ) + }, ) @pytest.mark.parametrize('image_extension', ['jpg', 'jpeg', 'png']) def test_upload_avatar_stream(mock_user, mock_box_session, image_extension): expected_url = mock_user.get_url('avatar') - upload_avatar_response = {'pic_urls': {'large': 'url1', 'preview': 'url2', 'small': 'url3'}} + upload_avatar_response = { + 'pic_urls': {'large': 'url1', 'preview': 'url2', 'small': 'url3'} + } mock_box_session.post.return_value.json.return_value = upload_avatar_response image_stream = io.BytesIO(b"some image stream") - avatar_urls = mock_user.upload_avatar_stream(image_stream=image_stream, image_extension=image_extension) + avatar_urls = mock_user.upload_avatar_stream( + image_stream=image_stream, image_extension=image_extension + ) assert avatar_urls == upload_avatar_response['pic_urls'] mock_box_session.post.assert_called_once_with( expected_url, - files={'pic': (f'avatar.{image_extension}', image_stream, f'image/{image_extension}')} + files={ + 'pic': ( + f'avatar.{image_extension}', + image_stream, + f'image/{image_extension}', + ) + }, ) diff --git a/test/unit/object/test_web_link.py b/test/unit/object/test_web_link.py index db8c1643a..1d3038133 100644 --- a/test/unit/object/test_web_link.py +++ b/test/unit/object/test_web_link.py @@ -18,7 +18,9 @@ def test_get(mock_box_session, test_web_link): } mock_box_session.get.return_value.json.return_value = mock_web_link web_link = test_web_link.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert web_link.id == mock_web_link['id'] assert web_link.type == mock_web_link['type'] assert web_link.url == mock_web_link['url'] @@ -40,7 +42,9 @@ def test_update(mock_box_session, test_web_link): } mock_box_session.put.return_value.json.return_value = mock_web_link web_link = test_web_link.update_info(data=data) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert web_link.id == mock_web_link['id'] assert web_link.type == mock_web_link['type'] assert web_link.url == mock_web_link['url'] @@ -51,17 +55,19 @@ def test_delete(mock_box_session, test_web_link): web_link_id = test_web_link.object_id expected_url = f'{API.BASE_API_URL}/web_links/{web_link_id}' test_web_link.delete() - mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={}) + mock_box_session.delete.assert_called_once_with( + expected_url, expect_json_response=False, headers=None, params={} + ) def test_get_shared_link( - test_web_link, - mock_box_session, - shared_link_access, - shared_link_unshared_at, - shared_link_password, - shared_link_vanity_name, - test_url, + test_web_link, + mock_box_session, + shared_link_access, + shared_link_unshared_at, + shared_link_password, + shared_link_vanity_name, + test_url, ): # pylint:disable=redefined-outer-name, protected-access expected_url = test_web_link.get_url() @@ -76,7 +82,9 @@ def test_get_shared_link( if shared_link_access is not None: expected_data['shared_link']['access'] = shared_link_access if shared_link_unshared_at is not SDK_VALUE_NOT_SET: - expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format(shared_link_unshared_at) + expected_data['shared_link']['unshared_at'] = normalize_date_to_rfc3339_format( + shared_link_unshared_at + ) if shared_link_password is not None: expected_data['shared_link']['password'] = shared_link_password if shared_link_vanity_name is not None: @@ -98,9 +106,9 @@ def test_get_shared_link( def test_clear_unshared_at_for_shared_link( - test_web_link, - mock_box_session, - test_url, + test_web_link, + mock_box_session, + test_url, ): expected_url = test_web_link.get_url() mock_box_session.put.return_value.json.return_value = { @@ -111,7 +119,11 @@ def test_clear_unshared_at_for_shared_link( 'unshared_at': None, }, } - expected_data = {'shared_link': {'unshared_at': None, }, } + expected_data = { + 'shared_link': { + 'unshared_at': None, + }, + } shared_link = test_web_link.get_shared_link(unshared_at=None) mock_box_session.put.assert_called_once_with( expected_url, diff --git a/test/unit/object/test_webhook.py b/test/unit/object/test_webhook.py index e598ee79b..19f946db6 100644 --- a/test/unit/object/test_webhook.py +++ b/test/unit/object/test_webhook.py @@ -16,15 +16,17 @@ def delete_webhook_response(): def test_delete_webhook_return_the_correct_response( - test_webhook, - mock_box_session, - delete_webhook_response, + test_webhook, + mock_box_session, + delete_webhook_response, ): # pylint:disable=redefined-outer-name mock_box_session.delete.return_value = delete_webhook_response response = test_webhook.delete() expected_url = f'{API.BASE_API_URL}/webhooks/{test_webhook.object_id}' - mock_box_session.delete.assert_called_once_with(expected_url, params={}, expect_json_response=False, headers=None) + mock_box_session.delete.assert_called_once_with( + expected_url, params={}, expect_json_response=False, headers=None + ) assert response is True @@ -38,7 +40,9 @@ def test_get(test_webhook, mock_box_session): 'triggers': ['FILE.UPLOADED'], } webhook = test_webhook.get() - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None) + mock_box_session.get.assert_called_once_with( + expected_url, headers=None, params=None + ) assert isinstance(webhook, Webhook) assert webhook.object_type == test_webhook.object_type assert webhook.object_id == test_webhook.object_id @@ -53,14 +57,16 @@ def test_update(test_webhook, mock_box_session): 'type': test_webhook.object_type, 'id': test_webhook.object_id, 'address': 'https://testnotification.com', - 'triggers': ['FILE.DOWNLOADED'] + 'triggers': ['FILE.DOWNLOADED'], } data = { 'address': 'https://testnotification.com', 'triggers': ['FILE.DOWNLOADED'], } webhook = test_webhook.update_info(data=data) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None) + mock_box_session.put.assert_called_once_with( + expected_url, data=json.dumps(data), headers=None, params=None + ) assert isinstance(webhook, Webhook) assert webhook.type == test_webhook.object_type assert webhook.id == test_webhook.object_id @@ -78,9 +84,11 @@ def test_update(test_webhook, mock_box_session): ('1', 'HmacSHA256', None, None, False), ('2', 'HmacSHA256', 'SamplePrimaryKey', 'SampleSecondaryKey', False), ('1', 'WrongAlgorithm', 'SamplePrimaryKey', 'SampleSecondaryKey', False), - ] + ], ) -def test_validate_message(signature_version, signature_algorithm, primary_key, secondary_key, expected_result): +def test_validate_message( + signature_version, signature_algorithm, primary_key, secondary_key, expected_result +): # pylint: disable=C0301 body = b'{"type":"webhook_event","webhook":{"id":"1234567890"},"trigger":"FILE.UPLOADED","source":{"id":"1234567890","type":"file","name":"Test.txt"}}' headers = { diff --git a/test/unit/pagination/box_object_collection_test_base.py b/test/unit/pagination/box_object_collection_test_base.py index 8645f8bc4..fa54e6434 100644 --- a/test/unit/pagination/box_object_collection_test_base.py +++ b/test/unit/pagination/box_object_collection_test_base.py @@ -23,13 +23,15 @@ def translator(): def entries(self): all_entries = [] for i in range(self.NUM_ENTRIES): - all_entries.append({ - "type": "file", - "id": str(1000 + i), - "sequence_id": str(i), - "etag": str(10 + i), - "name": f"file_{i}.txt", - }) + all_entries.append( + { + "type": "file", + "id": str(1000 + i), + "sequence_id": str(i), + "etag": str(10 + i), + "name": f"file_{i}.txt", + } + ) return all_entries @abstractmethod @@ -44,18 +46,18 @@ def mock_session(self, translator, mock_items_response): @abstractmethod def _object_collection_instance( - self, - session: 'Session', - limit: int, - return_full_pages: bool = False, - starting_pointer: Any = None + self, + session: 'Session', + limit: int, + return_full_pages: bool = False, + starting_pointer: Any = None, ) -> 'BoxObjectCollection': raise NotImplementedError @staticmethod def _assert_items_dict_and_objects_same( - expected_items_dict: Union[list, dict], - returned_item_objects: List['BaseObject'] + expected_items_dict: Union[list, dict], + returned_item_objects: List['BaseObject'], ) -> None: """ A fixture very specific to this test class. Asserts that the list of items in dictionary form are the @@ -66,18 +68,26 @@ def _assert_items_dict_and_objects_same( """ expected_num = len(expected_items_dict) actual_num = len(returned_item_objects) - assert actual_num == expected_num, f'Expected {expected_num} items, got {actual_num}' + assert ( + actual_num == expected_num + ), f'Expected {expected_num} items, got {actual_num}' returned_item_names = [item.name for item in returned_item_objects] for expected_item_dict in expected_items_dict: - assert expected_item_dict['name'] in returned_item_names, f'Missing item: {expected_item_dict["name"]}' + assert ( + expected_item_dict['name'] in returned_item_names + ), f'Missing item: {expected_item_dict["name"]}' @pytest.mark.parametrize('return_full_pages', (True, False)) @pytest.mark.parametrize('limit', (1, 3, 5, NUM_ENTRIES, 1000)) - def test_object_collection_pages_through_all_entries(self, mock_session, entries, limit, return_full_pages): + def test_object_collection_pages_through_all_entries( + self, mock_session, entries, limit, return_full_pages + ): """ Tests the basic iteration functionality of the box object collection. """ - object_collection = self._object_collection_instance(mock_session, limit, return_full_pages) + object_collection = self._object_collection_instance( + mock_session, limit, return_full_pages + ) iterated_items = [] for item_or_page in object_collection: if return_full_pages: @@ -86,7 +96,9 @@ def test_object_collection_pages_through_all_entries(self, mock_session, entries iterated_items.append(item_or_page) self._assert_items_dict_and_objects_same(entries, iterated_items) - def test_new_object_collection_starts_off_from_last_pointer(self, mock_session, entries): + def test_new_object_collection_starts_off_from_last_pointer( + self, mock_session, entries + ): """ Start paging with one object collection instance, and then finish paging with a new object collection instance, starting off from the next_pointer() of the previous instance. @@ -95,16 +107,15 @@ def test_new_object_collection_starts_off_from_last_pointer(self, mock_session, instance. """ iterated_items = [] - object_collection = self._object_collection_instance(mock_session, limit=5, return_full_pages=True) + object_collection = self._object_collection_instance( + mock_session, limit=5, return_full_pages=True + ) iterated_items.extend(object_collection.next()) next_pointer = object_collection.next_pointer() # Create a new object collection, starting off where the previous collection left off. new_object_collection = self._object_collection_instance( - mock_session, - limit=5, - return_full_pages=True, - starting_pointer=next_pointer + mock_session, limit=5, return_full_pages=True, starting_pointer=next_pointer ) for page in new_object_collection: iterated_items.extend(page) diff --git a/test/unit/pagination/test_limit_offset_based_object_collection.py b/test/unit/pagination/test_limit_offset_based_object_collection.py index 0d159279e..8938fb8d5 100644 --- a/test/unit/pagination/test_limit_offset_based_object_collection.py +++ b/test/unit/pagination/test_limit_offset_based_object_collection.py @@ -4,7 +4,9 @@ import pytest from boxsdk.network.default_network import DefaultNetworkResponse -from boxsdk.pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection +from boxsdk.pagination.limit_offset_based_object_collection import ( + LimitOffsetBasedObjectCollection, +) from boxsdk.session.box_response import BoxResponse from boxsdk.session.session import Session from .box_object_collection_test_base import BoxObjectCollectionTestBase @@ -16,13 +18,14 @@ class TestLimitOffsetBasedObjectCollection(BoxObjectCollectionTestBase): @pytest.fixture() def mock_items_response(self, entries): """Baseclass override.""" + # pylint:disable=redefined-outer-name def get_response(limit, offset): mock_box_response = Mock(BoxResponse) mock_network_response = Mock(DefaultNetworkResponse) mock_box_response.network_response = mock_network_response mock_box_response.json.return_value = mock_json = { - 'entries': entries[offset:limit + offset], + 'entries': entries[offset : limit + offset], 'total_count': len(entries), 'limit': limit, } @@ -30,15 +33,14 @@ def get_response(limit, offset): mock_box_response.status_code = 200 mock_box_response.ok = True return mock_box_response + return get_response @pytest.fixture() def mock_session(self, translator, mock_items_response): """Baseclass override.""" mock_box_session = Mock(Session) - type(mock_box_session).translator = PropertyMock( - return_value=translator - ) + type(mock_box_session).translator = PropertyMock(return_value=translator) def mock_items_side_effect(_, params): limit = min(params.get('limit', self.DEFAULT_LIMIT), self.DEFAULT_LIMIT) @@ -61,7 +63,9 @@ def mock_items_side_effect(_, params): mock_session.get.side_effect = mock_items_side_effect return mock_session - def _object_collection_instance(self, session, limit=None, return_full_pages=False, starting_pointer=None): + def _object_collection_instance( + self, session, limit=None, return_full_pages=False, starting_pointer=None + ): """Baseclass override.""" if starting_pointer is None: starting_pointer = 0 @@ -74,7 +78,9 @@ def _object_collection_instance(self, session, limit=None, return_full_pages=Fal ) @pytest.mark.parametrize('return_full_pages', (True, False)) - def test_object_collection_sets_next_pointer_correctly(self, mock_session, return_full_pages): + def test_object_collection_sets_next_pointer_correctly( + self, mock_session, return_full_pages + ): page_size = 10 object_collection = LimitOffsetBasedObjectCollection( mock_session, @@ -91,7 +97,9 @@ def test_object_collection_sets_next_pointer_correctly(self, mock_session, retur list(object_collection) assert object_collection.next_pointer() == self.NUM_ENTRIES - def test_object_collection_raises_stop_iteration_when_starting_offset_is_too_far(self, mock_session, entries): + def test_object_collection_raises_stop_iteration_when_starting_offset_is_too_far( + self, mock_session, entries + ): """ If the specified initial offset for the object collection is higher than the total number of items, then the first call to next() on the object collection should raise a StopIteration. @@ -101,23 +109,35 @@ def test_object_collection_raises_stop_iteration_when_starting_offset_is_too_far mock_session, limit=5, return_full_pages=False, - starting_pointer=starting_offset + starting_pointer=starting_offset, ) with pytest.raises(StopIteration): object_collection.next() - def test_object_collection_sets_limit_to_returned_value_if_originally_none(self, mock_session): + def test_object_collection_sets_limit_to_returned_value_if_originally_none( + self, mock_session + ): # No limit specified object_collection = self._object_collection_instance(mock_session) object_collection.next() - assert object_collection._limit == self.DEFAULT_LIMIT # pylint:disable=protected-access + assert ( + object_collection._limit == self.DEFAULT_LIMIT + ) # pylint:disable=protected-access - def test_object_collection_sets_limit_to_returned_value_if_originally_too_high(self, mock_session): - object_collection = self._object_collection_instance(mock_session, limit=(1 + self.DEFAULT_LIMIT)) + def test_object_collection_sets_limit_to_returned_value_if_originally_too_high( + self, mock_session + ): + object_collection = self._object_collection_instance( + mock_session, limit=(1 + self.DEFAULT_LIMIT) + ) object_collection.next() - assert object_collection._limit == self.DEFAULT_LIMIT # pylint:disable=protected-access + assert ( + object_collection._limit == self.DEFAULT_LIMIT + ) # pylint:disable=protected-access - def test_box_returning_bogus_limit_raises_runtime_error(self, mock_session_with_bogus_limit, entries): + def test_box_returning_bogus_limit_raises_runtime_error( + self, mock_session_with_bogus_limit, entries + ): """ Confirm that the SDK raises a RuntimeError is box.com happens to return a bogus `limit` in the response """ @@ -126,7 +146,7 @@ def test_box_returning_bogus_limit_raises_runtime_error(self, mock_session_with_ mock_session_with_bogus_limit, limit=5, return_full_pages=False, - starting_pointer=starting_offset + starting_pointer=starting_offset, ) with pytest.raises(RuntimeError): object_collection.next() diff --git a/test/unit/pagination/test_marker_based_object_collection.py b/test/unit/pagination/test_marker_based_object_collection.py index 1d074fb3d..735713c8e 100644 --- a/test/unit/pagination/test_marker_based_object_collection.py +++ b/test/unit/pagination/test_marker_based_object_collection.py @@ -27,6 +27,7 @@ def next_marker_value_for_last_page(request): @pytest.fixture() def mock_items_response(self, entries, next_marker_value_for_last_page): """Baseclass override.""" + # pylint:disable=redefined-outer-name,arguments-differ def get_response(limit, marker): mock_box_response = Mock(BoxResponse) @@ -39,7 +40,7 @@ def get_response(limit, marker): offset = 0 if marker is not None: offset = int(marker.split('_')[1]) - mock_json['entries'] = entries[offset:limit + offset] + mock_json['entries'] = entries[offset : limit + offset] # A next_marker is only returned if there are more pages left. if (offset + limit) < len(entries): @@ -52,6 +53,7 @@ def get_response(limit, marker): mock_box_response.status_code = 200 mock_box_response.ok = True return mock_box_response + return get_response @pytest.fixture() @@ -69,12 +71,12 @@ def mock_items_side_effect(_, params): return mock_box_session def _object_collection_instance( # pylint:disable=arguments-differ - self, - session, - limit, - return_full_pages=False, - starting_pointer=None, - supports_limit_offset_paging=False + self, + session, + limit, + return_full_pages=False, + starting_pointer=None, + supports_limit_offset_paging=False, ): """Baseclass override.""" return MarkerBasedObjectCollection( @@ -87,18 +89,24 @@ def _object_collection_instance( # pylint:disable=arguments-differ ) @pytest.mark.parametrize('return_full_pages', (True, False)) - def test_object_collection_sets_next_pointer_correctly(self, mock_session, return_full_pages): - object_collection = self._object_collection_instance(mock_session, limit=5, return_full_pages=return_full_pages) + def test_object_collection_sets_next_pointer_correctly( + self, mock_session, return_full_pages + ): + object_collection = self._object_collection_instance( + mock_session, limit=5, return_full_pages=return_full_pages + ) assert object_collection.next_pointer() is None object_collection.next() assert object_collection.next_pointer() == 'marker_5' @pytest.mark.parametrize('supports_limit_offset_paging', (True, False)) - def test_object_collection_specifies_marker_param(self, mock_session, supports_limit_offset_paging): + def test_object_collection_specifies_marker_param( + self, mock_session, supports_limit_offset_paging + ): object_collection = self._object_collection_instance( mock_session, limit=100, - supports_limit_offset_paging=supports_limit_offset_paging + supports_limit_offset_paging=supports_limit_offset_paging, ) object_collection.next() diff --git a/test/unit/pagination/test_page.py b/test/unit/pagination/test_page.py index fd99b4a1e..09efec61e 100644 --- a/test/unit/pagination/test_page.py +++ b/test/unit/pagination/test_page.py @@ -16,19 +16,15 @@ def translator(): @pytest.fixture() def mock_session(translator): mock_box_session = Mock(Session) - type(mock_box_session).translator = PropertyMock( - return_value=translator - ) + type(mock_box_session).translator = PropertyMock(return_value=translator) return mock_box_session @pytest.fixture() def page_builder(mock_session): def factory_function(response): - return Page( - session=mock_session, - response_object=response - ) + return Page(session=mock_session, response_object=response) + return factory_function @@ -55,14 +51,14 @@ def test_getitem(page_builder, item_checker): "id": "192429928", "sequence_id": "1", "etag": "1", - "name": "Stephen Curry Three Pointers" + "name": "Stephen Curry Three Pointers", } entry_2 = { "type": "file", "id": "818853862", "sequence_id": "0", "etag": "0", - "name": "Warriors.jpg" + "name": "Warriors.jpg", } response_entries = [entry_1, entry_2] response = {"entries": response_entries} @@ -86,17 +82,22 @@ def test_getitem_past_length_raises(page_builder): @pytest.mark.parametrize('length', (0, 1, 10)) def test_len(page_builder, length): response_entries = [object() for _ in range(length)] - response = { - "entries": response_entries - } + response = {"entries": response_entries} page = page_builder(response=response) assert len(page) == length def test_translation_of_page_entries(page_builder, mock_session, item_checker): tested_item_types = ['folder', 'file', 'user', 'collaboration', 'group', 'foobar'] - response_entries = [{'type': item_type_string, 'id': str(i)} for i, item_type_string in enumerate(tested_item_types)] + response_entries = [ + {'type': item_type_string, 'id': str(i)} + for i, item_type_string in enumerate(tested_item_types) + ] response = {"entries": response_entries} page = page_builder(response=response) for i, item in enumerate(page): - item_checker(mock_session.translator.get(response_entries[i]['type']), response_entries[i], item) + item_checker( + mock_session.translator.get(response_entries[i]['type']), + response_entries[i], + item, + ) diff --git a/test/unit/session/test_session.py b/test/unit/session/test_session.py index ddeaf3a1a..932b42650 100644 --- a/test/unit/session/test_session.py +++ b/test/unit/session/test_session.py @@ -3,7 +3,11 @@ from numbers import Number import os from unittest.mock import MagicMock, Mock, PropertyMock, call, patch, ANY -from requests.exceptions import RequestException, SSLError, ConnectionError as RequestsConnectionError +from requests.exceptions import ( + RequestException, + SSLError, + ConnectionError as RequestsConnectionError, +) from requests_toolbelt import MultipartEncoder import pytest @@ -50,41 +54,58 @@ def unauthorized_session(mock_network_layer, translator): @pytest.fixture def box_session(mock_oauth, mock_network_layer, translator): # pylint:disable=redefined-outer-name - return AuthorizedSession(oauth=mock_oauth, network_layer=mock_network_layer, translator=translator) + return AuthorizedSession( + oauth=mock_oauth, network_layer=mock_network_layer, translator=translator + ) @pytest.fixture -def ccg_auth(client_id, client_secret, mock_user_id, mock_enterprise_id, box_session) -> CCGAuth: +def ccg_auth( + client_id, client_secret, mock_user_id, mock_enterprise_id, box_session +) -> CCGAuth: # pylint:disable=protected-access - auth = CCGAuth(client_id=client_id, client_secret=client_secret, user=mock_user_id, enterprise_id=mock_enterprise_id) + auth = CCGAuth( + client_id=client_id, + client_secret=client_secret, + user=mock_user_id, + enterprise_id=mock_enterprise_id, + ) auth._session = box_session return auth -@pytest.mark.parametrize('test_method', [ - Session.get, - Session.post, - Session.put, - Session.delete, - Session.options, -]) +@pytest.mark.parametrize( + 'test_method', + [ + Session.get, + Session.post, + Session.put, + Session.delete, + Session.options, + ], +) def test_box_session_handles_unauthorized_response( - test_method, - box_session, - mock_oauth, - mock_network_layer, - unauthorized_response, - generic_successful_response, - test_url, + test_method, + box_session, + mock_oauth, + mock_network_layer, + unauthorized_response, + generic_successful_response, + test_url, ): # pylint:disable=redefined-outer-name def get_access_token_from_auth_object(): return mock_oauth.access_token - mock_network_layer.request.side_effect = mock_responses = [unauthorized_response, generic_successful_response] + mock_network_layer.request.side_effect = mock_responses = [ + unauthorized_response, + generic_successful_response, + ] for mock_response in mock_responses: - type(mock_response).access_token_used = PropertyMock(side_effect=get_access_token_from_auth_object) + type(mock_response).access_token_used = PropertyMock( + side_effect=get_access_token_from_auth_object + ) def refresh(access_token_used): assert access_token_used == mock_oauth.access_token @@ -97,30 +118,37 @@ def refresh(access_token_used): assert box_response.status_code == 200 -@pytest.mark.parametrize('test_method', [ - Session.get, - Session.post, - Session.put, - Session.delete, - Session.options, -]) +@pytest.mark.parametrize( + 'test_method', + [ + Session.get, + Session.post, + Session.put, + Session.delete, + Session.options, + ], +) @pytest.mark.parametrize('initial_access_token', [None]) def test_box_session_gets_access_token_before_request( - test_method, - box_session, - mock_oauth, - mock_network_layer, - generic_successful_response, - test_url, + test_method, + box_session, + mock_oauth, + mock_network_layer, + generic_successful_response, + test_url, ): # pylint:disable=redefined-outer-name def get_access_token_from_auth_object(): return mock_oauth.access_token - mock_network_layer.request.side_effect = mock_responses = [generic_successful_response] + mock_network_layer.request.side_effect = mock_responses = [ + generic_successful_response + ] for mock_response in mock_responses: - type(mock_response).access_token_used = PropertyMock(side_effect=get_access_token_from_auth_object) + type(mock_response).access_token_used = PropertyMock( + side_effect=get_access_token_from_auth_object + ) def refresh(access_token_used): assert access_token_used == mock_oauth.access_token @@ -133,21 +161,24 @@ def refresh(access_token_used): assert box_response.status_code == 200 -@pytest.mark.parametrize('test_method', [ - Session.get, - Session.post, - Session.put, - Session.delete, - Session.options, - partial(Session.request, method='head'), -]) +@pytest.mark.parametrize( + 'test_method', + [ + Session.get, + Session.post, + Session.put, + Session.delete, + Session.options, + partial(Session.request, method='head'), + ], +) def test_box_session_retries_response_after_retry_after( - test_method, - box_session, - mock_network_layer, - retry_after_response, - generic_successful_response, - test_url, + test_method, + box_session, + mock_network_layer, + retry_after_response, + generic_successful_response, + test_url, ): # pylint:disable=redefined-outer-name try: @@ -156,8 +187,13 @@ def test_box_session_retries_response_after_retry_after( except AttributeError: pass - mock_network_layer.request.side_effect = [retry_after_response, generic_successful_response] - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.request.side_effect = [ + retry_after_response, + generic_successful_response, + ] + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) with patch('random.uniform', return_value=0.68): box_response = test_method(box_session, url=test_url) @@ -167,25 +203,34 @@ def test_box_session_retries_response_after_retry_after( assert round(mock_network_layer.retry_after.call_args[0][0], 4) == 1 -@pytest.mark.parametrize('test_method', [ - Session.get, - Session.post, - Session.put, - Session.delete, - Session.options, - partial(Session.request, method='head'), -]) +@pytest.mark.parametrize( + 'test_method', + [ + Session.get, + Session.post, + Session.put, + Session.delete, + Session.options, + partial(Session.request, method='head'), + ], +) def test_box_session_retries_request_after_server_error( - test_method, - box_session, - mock_network_layer, - server_error_response, - generic_successful_response, - test_url, + test_method, + box_session, + mock_network_layer, + server_error_response, + generic_successful_response, + test_url, ): # pylint:disable=redefined-outer-name - mock_network_layer.request.side_effect = [server_error_response, server_error_response, generic_successful_response] - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.request.side_effect = [ + server_error_response, + server_error_response, + generic_successful_response, + ] + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) with patch('random.uniform', return_value=0.68): box_response = test_method(box_session, url=test_url) @@ -200,10 +245,21 @@ def test_box_session_retries_request_after_server_error( assert round(mock_network_layer.retry_after.call_args_list[1][0][0], 4) == 2.36 -def test_box_session_seeks_file_after_retry(box_session, mock_network_layer, server_error_response, generic_successful_response, test_url): +def test_box_session_seeks_file_after_retry( + box_session, + mock_network_layer, + server_error_response, + generic_successful_response, + test_url, +): # pylint:disable=redefined-outer-name - mock_network_layer.request.side_effect = [server_error_response, generic_successful_response] - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.request.side_effect = [ + server_error_response, + generic_successful_response, + ] + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) mock_file_1, mock_file_2 = MagicMock(IOBase), MagicMock(IOBase) mock_file_1.tell.return_value = 0 mock_file_2.tell.return_value = 3 @@ -223,24 +279,43 @@ def test_box_session_seeks_file_after_retry(box_session, mock_network_layer, ser mock_file_2.seek.assert_has_calls([call(3), call(3)]) -def test_box_session_raises_for_non_json_response(box_session, mock_network_layer, non_json_response, generic_successful_response, test_url): +def test_box_session_raises_for_non_json_response( + box_session, + mock_network_layer, + non_json_response, + generic_successful_response, + test_url, +): # pylint:disable=redefined-outer-name - mock_network_layer.request.side_effect = [non_json_response, generic_successful_response] - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.request.side_effect = [ + non_json_response, + generic_successful_response, + ] + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) box_session.get(url=test_url) -def test_box_session_raises_for_non_json_response_after_retry(box_session, mock_network_layer, non_json_response, test_url): +def test_box_session_raises_for_non_json_response_after_retry( + box_session, mock_network_layer, non_json_response, test_url +): # pylint:disable=redefined-outer-name - mock_network_layer.request.side_effect = [non_json_response] * (API.MAX_RETRY_ATTEMPTS + 1) - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.request.side_effect = [non_json_response] * ( + API.MAX_RETRY_ATTEMPTS + 1 + ) + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) with pytest.raises(BoxAPIException): box_session.get(url=test_url) -def test_box_session_raises_for_failed_response(box_session, mock_network_layer, bad_network_response, test_url): +def test_box_session_raises_for_failed_response( + box_session, mock_network_layer, bad_network_response, test_url +): # pylint:disable=redefined-outer-name mock_network_layer.request.side_effect = [bad_network_response] @@ -248,47 +323,73 @@ def test_box_session_raises_for_failed_response(box_session, mock_network_layer, box_session.get(url=test_url) -@pytest.mark.parametrize('exc_message', [ - 'Connection aborted', - "Connection broken: ConnectionResetError(54, 'Connection reset by peer')" -]) +@pytest.mark.parametrize( + 'exc_message', + [ + 'Connection aborted', + "Connection broken: ConnectionResetError(54, 'Connection reset by peer')", + ], +) def test_box_session_retries_connection_aborted_exception( - box_session, mock_network_layer, generic_successful_request_response, test_url, exc_message + box_session, + mock_network_layer, + generic_successful_request_response, + test_url, + exc_message, ): - mock_network_layer.request.side_effect = [RequestsConnectionError(exc_message), generic_successful_request_response] - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.request.side_effect = [ + RequestsConnectionError(exc_message), + generic_successful_request_response, + ] + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) box_response = box_session.get(url=test_url) assert box_response.status_code == 200 -def test_box_session_retries_connection_aborted_exception_on_ccg_auth_call(successful_token_response, ccg_auth, mock_user_id, access_token): +def test_box_session_retries_connection_aborted_exception_on_ccg_auth_call( + successful_token_response, ccg_auth, mock_user_id, access_token +): # pylint:disable=protected-access - ccg_auth._session._network_layer.request.side_effect = [RequestsConnectionError('Connection aborted'), successful_token_response] - ccg_auth._session._network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ccg_auth._session._network_layer.request.side_effect = [ + RequestsConnectionError('Connection aborted'), + successful_token_response, + ] + ccg_auth._session._network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) new_access_token = ccg_auth.authenticate_user(mock_user_id) assert new_access_token == access_token -def test_box_session_retries_requests_library_exceptions_only_once(box_session, mock_network_layer, test_url, generic_successful_request_response): +def test_box_session_retries_requests_library_exceptions_only_once( + box_session, mock_network_layer, test_url, generic_successful_request_response +): mock_network_layer.request.side_effect = [ RequestException('Connection aborted'), RequestException('Connection aborted'), - generic_successful_request_response + generic_successful_request_response, ] - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) with pytest.raises(RequestException): box_session.get(url=test_url) def test_box_session_raises_requests_library_exception_when_set_no_retries( - box_session, mock_network_layer, test_url, generic_successful_request_response + box_session, mock_network_layer, test_url, generic_successful_request_response ): API.MAX_RETRY_ATTEMPTS = 0 try: - mock_network_layer.request.side_effect = [RequestException('Connection aborted'), generic_successful_request_response] + mock_network_layer.request.side_effect = [ + RequestException('Connection aborted'), + generic_successful_request_response, + ] with pytest.raises(RequestException): box_session.get(url=test_url) @@ -297,7 +398,11 @@ def test_box_session_raises_requests_library_exception_when_set_no_retries( def test_box_session_retries_and_reauthenticates_on_violation_of_protocol_exception( - box_session, mock_oauth, mock_network_layer, generic_successful_request_response, test_url + box_session, + mock_oauth, + mock_network_layer, + generic_successful_request_response, + test_url, ): def refresh(access_token_used): assert access_token_used == mock_oauth.access_token @@ -305,27 +410,41 @@ def refresh(access_token_used): return (mock_oauth.access_token, None) mock_network_layer.request.side_effect = [ - SSLError("SSLError(SSLEOFError(8, 'EOF occurred in violation of protocol (_ssl.c:2396)')))"), - generic_successful_request_response + SSLError( + "SSLError(SSLEOFError(8, 'EOF occurred in violation of protocol (_ssl.c:2396)')))" + ), + generic_successful_request_response, ] - mock_network_layer.retry_after.side_effect = lambda delay, request, *args, **kwargs: request(*args, **kwargs) + mock_network_layer.retry_after.side_effect = ( + lambda delay, request, *args, **kwargs: request(*args, **kwargs) + ) mock_oauth.refresh.side_effect = refresh box_session.get(url=test_url) assert mock_network_layer.request.call_count == 2 - assert mock_network_layer.request.mock_calls[0][2]['access_token'] == 'fake_access_token' - assert mock_network_layer.request.mock_calls[1][2]['access_token'] == 'fake_new_access_token' + assert ( + mock_network_layer.request.mock_calls[0][2]['access_token'] + == 'fake_access_token' + ) + assert ( + mock_network_layer.request.mock_calls[1][2]['access_token'] + == 'fake_new_access_token' + ) -def test_box_session_does_not_retry_other_requests_library_exceptions_than_specified(box_session, mock_network_layer, test_url): +def test_box_session_does_not_retry_other_requests_library_exceptions_than_specified( + box_session, mock_network_layer, test_url +): mock_network_layer.request.side_effect = [RequestException('Unknown error')] with pytest.raises(RequestException): box_session.get(url=test_url) -def test_box_session_raises_for_failed_response_with_error_and_error_description(box_session, mock_network_layer, bad_network_response_400, test_url): +def test_box_session_raises_for_failed_response_with_error_and_error_description( + box_session, mock_network_layer, bad_network_response_400, test_url +): mock_network_layer.request.side_effect = [bad_network_response_400] try: box_session.get(url=test_url) @@ -335,7 +454,9 @@ def test_box_session_raises_for_failed_response_with_error_and_error_description assert exception.message == 'Example Error Description' -def test_box_session_raises_for_failed_non_json_response(box_session, mock_network_layer, failed_non_json_response, test_url): +def test_box_session_raises_for_failed_non_json_response( + box_session, mock_network_layer, failed_non_json_response, test_url +): # pylint:disable=redefined-outer-name mock_network_layer.request.side_effect = [failed_non_json_response] @@ -353,7 +474,9 @@ def test_box_response_properties_pass_through_to_network_response_properties(): assert box_result.network_response == mock_network_response -def test_translator(box_session, translator, default_translator, original_default_translator): +def test_translator( + box_session, translator, default_translator, original_default_translator +): assert isinstance(box_session.translator, Translator) assert box_session.translator == default_translator if translator: @@ -373,14 +496,18 @@ class Foo: assert (set(box_session.translator) - set(default_translator)) == {item_type} -def test_session_uses_global_config(box_session, mock_network_layer, generic_successful_response, monkeypatch): +def test_session_uses_global_config( + box_session, mock_network_layer, generic_successful_response, monkeypatch +): mock_network_layer.request.side_effect = generic_successful_response example_dot_com = 'https://example.com/' monkeypatch.setattr(API, 'BASE_API_URL', example_dot_com) assert example_dot_com in box_session.get_url('foo', 'bar') -def test_session_uses_local_config(box_session, mock_network_layer, generic_successful_response, monkeypatch): +def test_session_uses_local_config( + box_session, mock_network_layer, generic_successful_response, monkeypatch +): mock_network_layer.request.side_effect = generic_successful_response example_dot_com = 'https://example.com/' box_session.api_config.BASE_API_URL = example_dot_com @@ -396,11 +523,15 @@ def test_session_uses_local_config(box_session, mock_network_layer, generic_succ (2, '', 4.72), (3, '', 9.44), (4, '', 18.88), - ] + ], ) -def test_get_retry_after_time(box_session, attempt_number, retry_after_header, expected_result): +def test_get_retry_after_time( + box_session, attempt_number, retry_after_header, expected_result +): with patch('random.uniform', return_value=0.68): - retry_time = box_session.get_retry_after_time(attempt_number, retry_after_header) # pylint: disable=protected-access + retry_time = box_session.get_retry_after_time( + attempt_number, retry_after_header + ) # pylint: disable=protected-access retry_time = round(retry_time, 4) assert retry_time == expected_result @@ -408,18 +539,33 @@ def test_get_retry_after_time(box_session, attempt_number, retry_after_header, e @pytest.mark.parametrize( 'test_proxy_url,test_proxy_auth,expected_proxy_dict', [ - ('http://example-proxy.com', {'user': 'test_user', 'password': 'test_password', }, - {'http': 'http://test_user:test_password@example-proxy.com', 'https': 'http://test_user:test_password@example-proxy.com'}), - ('http://example-proxy.com', None, {'http': 'http://example-proxy.com', 'https': 'http://example-proxy.com'}), - ] + ( + 'http://example-proxy.com', + { + 'user': 'test_user', + 'password': 'test_password', + }, + { + 'http': 'http://test_user:test_password@example-proxy.com', + 'https': 'http://test_user:test_password@example-proxy.com', + }, + ), + ( + 'http://example-proxy.com', + None, + {'http': 'http://example-proxy.com', 'https': 'http://example-proxy.com'}, + ), + ], ) def test_proxy_attaches_to_request_correctly( - box_session, - monkeypatch, - mock_network_layer, - generic_successful_response, - test_proxy_url, test_proxy_auth, - expected_proxy_dict): + box_session, + monkeypatch, + mock_network_layer, + generic_successful_response, + test_proxy_url, + test_proxy_auth, + expected_proxy_dict, +): monkeypatch.setattr(Proxy, 'URL', test_proxy_url) monkeypatch.setattr(Proxy, 'AUTH', test_proxy_auth) mock_network_layer.request.side_effect = [generic_successful_response] @@ -434,7 +580,9 @@ def test_proxy_attaches_to_request_correctly( ) -def test_proxy_malformed_dict_does_not_attach(box_session, monkeypatch, mock_network_layer, generic_successful_response): +def test_proxy_malformed_dict_does_not_attach( + box_session, monkeypatch, mock_network_layer, generic_successful_response +): test_proxy_url = 'http://example.com' test_proxy_auth = { 'foo': 'bar', @@ -445,8 +593,11 @@ def test_proxy_malformed_dict_does_not_attach(box_session, monkeypatch, mock_net with pytest.raises(BoxException) as exc_info: box_session.request('GET', test_proxy_url) assert isinstance(exc_info.value, BoxException) - assert exc_info.value.args[0] == "The proxy auth dict you provided does not match pattern " \ - "{'user': 'example_user', 'password': 'example_password'}" + assert ( + exc_info.value.args[0] + == "The proxy auth dict you provided does not match pattern " + "{'user': 'example_user', 'password': 'example_password'}" + ) def test_proxy_network_config_property(box_session): @@ -454,7 +605,8 @@ def test_proxy_network_config_property(box_session): def test_multipart_request_with_disabled_streaming_file_content( - box_session, mock_network_layer, generic_successful_response): + box_session, mock_network_layer, generic_successful_response +): test_url = 'https://example.com' file_bytes = os.urandom(1024) mock_network_layer.request.side_effect = [generic_successful_response] @@ -462,7 +614,7 @@ def test_multipart_request_with_disabled_streaming_file_content( url=test_url, files={'file': ('unused', BytesIO(file_bytes))}, data={'attributes': '{"name": "test_file"}'}, - stream_file_content=False + stream_file_content=False, ) mock_network_layer.request.assert_called_once_with( 'POST', @@ -476,7 +628,8 @@ def test_multipart_request_with_disabled_streaming_file_content( def test_multipart_request_with_enabled_streaming_file_content( - box_session, mock_network_layer, generic_successful_response): + box_session, mock_network_layer, generic_successful_response +): test_url = 'https://example.com' file_bytes = os.urandom(1024) mock_network_layer.request.side_effect = [generic_successful_response] @@ -484,7 +637,7 @@ def test_multipart_request_with_enabled_streaming_file_content( url=test_url, files={'file': ('unused', BytesIO(file_bytes))}, data={'attributes': '{"name": "test_file"}'}, - stream_file_content=True + stream_file_content=True, ) call_args = mock_network_layer.request.call_args[0] call_kwargs = mock_network_layer.request.call_args[1] diff --git a/test/unit/test_exception.py b/test/unit/test_exception.py index 7b100daeb..dbc109d45 100644 --- a/test/unit/test_exception.py +++ b/test/unit/test_exception.py @@ -32,7 +32,9 @@ def test_box_api_exception(): assert box_exception.url == url assert box_exception.method == method assert box_exception.context_info == context_info - assert str(box_exception) == f'''Message: {message} + assert ( + str(box_exception) + == f'''Message: {message} Status: {status} Code: {code} Request ID: {request_id} @@ -40,6 +42,7 @@ def test_box_api_exception(): URL: {url} Method: {method} Context Info: {context_info}''' + ) @pytest.mark.parametrize('has_network_response', [True, False]) @@ -49,7 +52,9 @@ def test_box_oauth_exception(has_network_response): url = 'https://example.com' method = 'GET' headers = {'header': 'value'} - network_response = Mock(DefaultNetworkResponse, headers=headers) if has_network_response else None + network_response = ( + Mock(DefaultNetworkResponse, headers=headers) if has_network_response else None + ) box_exception = BoxOAuthException( status, message=message, @@ -57,10 +62,13 @@ def test_box_oauth_exception(has_network_response): method=method, network_response=network_response, ) - assert str(box_exception) == f''' + assert ( + str(box_exception) + == f''' Message: {message} Status: {status} URL: {url} Method: {method} Headers: {headers if has_network_response else 'N/A'}''' + ) assert box_exception.network_response is network_response diff --git a/test/unit/util/test_api_call_decorator.py b/test/unit/util/test_api_call_decorator.py index 21b569834..741461318 100644 --- a/test/unit/util/test_api_call_decorator.py +++ b/test/unit/util/test_api_call_decorator.py @@ -38,7 +38,9 @@ def mock_cloneable(cloneable_subclass_with_api_call_method): class MockCloneable(cloneable_subclass_with_api_call_method, NonCallableMock): pass - return MockCloneable(spec_set=cloneable_subclass_with_api_call_method, name='Cloneable') + return MockCloneable( + spec_set=cloneable_subclass_with_api_call_method, name='Cloneable' + ) def test_api_call_is_decorator(): @@ -73,7 +75,9 @@ def func(self): obj.func() -def test_api_call_decorated_method_must_be_bound_to_an_instance_of_the_owner(mock_cloneable, api_call_method): +def test_api_call_decorated_method_must_be_bound_to_an_instance_of_the_owner( + mock_cloneable, api_call_method +): # pylint:disable=abstract-method class CloneableSubclass2(Cloneable): pass @@ -82,11 +86,15 @@ class CloneableSubclass2(Cloneable): api_call_method.__get__(mock_cloneable, CloneableSubclass2) -def test_api_call_decorated_method_returns_itself_when_bound_to_none(api_call_method, cloneable_subclass_with_api_call_method): +def test_api_call_decorated_method_returns_itself_when_bound_to_none( + api_call_method, cloneable_subclass_with_api_call_method +): assert api_call_method.__get__(None, Cloneable) is api_call_method assert not hasattr(api_call_method.__get__(None, Cloneable), '__self__') assert cloneable_subclass_with_api_call_method.api_call_method is api_call_method - assert not hasattr(cloneable_subclass_with_api_call_method.api_call_method, '__self__') + assert not hasattr( + cloneable_subclass_with_api_call_method.api_call_method, '__self__' + ) def test_api_call_decorated_method_binds_to_instance(mock_cloneable, api_call_method): @@ -96,18 +104,30 @@ def test_api_call_decorated_method_binds_to_instance(mock_cloneable, api_call_me assert mock_cloneable.api_call_method.__self__ is mock_cloneable -def test_api_call_decorated_method_delegates_to_wrapped_method(mock_cloneable, api_call_result): +def test_api_call_decorated_method_delegates_to_wrapped_method( + mock_cloneable, api_call_result +): args = (1, 2, 'ƒøø', 'bar') kwargs = {'bar': 'ƒøø'} - assert mock_cloneable.api_call_method(*args, **kwargs) == (mock_cloneable, args, kwargs, api_call_result) + assert mock_cloneable.api_call_method(*args, **kwargs) == ( + mock_cloneable, + args, + kwargs, + api_call_result, + ) def test_api_call_decorated_method_can_be_called_as_an_unbound_method_with_an_instance_as_the_first_argument( - mock_cloneable, - api_call_result, - cloneable_subclass_with_api_call_method, + mock_cloneable, + api_call_result, + cloneable_subclass_with_api_call_method, ): args = (1, 2, 'ƒøø', 'bar') kwargs = {'bar': 'ƒøø'} api_call_method = cloneable_subclass_with_api_call_method.api_call_method - assert api_call_method(mock_cloneable, *args, **kwargs) == (mock_cloneable, args, kwargs, api_call_result) + assert api_call_method(mock_cloneable, *args, **kwargs) == ( + mock_cloneable, + args, + kwargs, + api_call_result, + ) diff --git a/test/unit/util/test_datetime_formatter.py b/test/unit/util/test_datetime_formatter.py index f0b2c4789..980f007cf 100644 --- a/test/unit/util/test_datetime_formatter.py +++ b/test/unit/util/test_datetime_formatter.py @@ -51,7 +51,7 @@ def test_normalize_date_to_rfc3339_format_timezone_aware_datetime( ( "2035-03-04T10:14:24.000", "2035-03-04T10:14:24", - lazy_fixture('mock_timezone_naive_datetime_obj') + lazy_fixture('mock_timezone_naive_datetime_obj'), ), ) def test_add_timezone_info_when_timezone_naive_datetime_provided( diff --git a/test/unit/util/test_enum.py b/test/unit/util/test_enum.py index a5e5e2362..58e49c483 100644 --- a/test/unit/util/test_enum.py +++ b/test/unit/util/test_enum.py @@ -4,7 +4,6 @@ from boxsdk.util.enum import ExtendableEnumMeta - # pylint:disable=invalid-name # So that we can have class definitions as pytest function fixtures. @@ -55,7 +54,9 @@ class Enum2_2(Enum2): @pytest.fixture(scope='function') -def EnumBaseWithSubclassesDefined(EnumBase, Enum1, Enum2_1, Enum2_2): # pylint:disable=unused-argument +def EnumBaseWithSubclassesDefined( + EnumBase, Enum1, Enum2_1, Enum2_2 +): # pylint:disable=unused-argument return EnumBase @@ -76,7 +77,9 @@ def enum_member_value(request): @pytest.fixture(scope='function') def enum_members(Enum1, Enum2_1, Enum2_2): members = OrderedDict() - for enum_member_name, enum_class in zip(enum_member_names, [Enum1, Enum2_1, Enum2_2]): + for enum_member_name, enum_class in zip( + enum_member_names, [Enum1, Enum2_1, Enum2_2] + ): members[enum_member_name] = enum_class[enum_member_name] return members @@ -86,13 +89,16 @@ def enum_instance(enum_member_name, enum_members): return enum_members[enum_member_name] -def test_can_construct_enum_hierarchy(EnumBaseWithSubclassesDefined): # pylint:disable=unused-argument +def test_can_construct_enum_hierarchy( + EnumBaseWithSubclassesDefined, +): # pylint:disable=unused-argument pass def test_can_not_construct_non_leaf_enum_with_members(Enum1): with pytest.raises(TypeError): - class Enum1_1(Enum1): # pylint:disable=unused-variable + + class Enum1_1(Enum1): # pylint:disable=unused-variable pass @@ -119,7 +125,9 @@ def test_contains_enum_instances(EnumBaseWithSubclassesDefined, enum_instance): assert enum_instance in EnumBaseWithSubclassesDefined -def test_contains_returns_false_for_non_instances(EnumBaseWithSubclassesDefined, enum_member_name): +def test_contains_returns_false_for_non_instances( + EnumBaseWithSubclassesDefined, enum_member_name +): assert enum_member_name not in EnumBaseWithSubclassesDefined @@ -127,27 +135,37 @@ def test_getitem(EnumBaseWithSubclassesDefined, enum_member_name, enum_instance) assert EnumBaseWithSubclassesDefined[enum_member_name] == enum_instance -def test_getitem_raises_key_error_for_non_member_names(EnumBaseWithSubclassesDefined, enum_member_value): +def test_getitem_raises_key_error_for_non_member_names( + EnumBaseWithSubclassesDefined, enum_member_value +): with pytest.raises(KeyError): - EnumBaseWithSubclassesDefined[enum_member_value] # pylint:disable=pointless-statement + EnumBaseWithSubclassesDefined[ + enum_member_value + ] # pylint:disable=pointless-statement def test_getattr(EnumBaseWithSubclassesDefined, enum_member_name, enum_instance): assert getattr(EnumBaseWithSubclassesDefined, enum_member_name) == enum_instance -def test_getattr_raises_attribute_error_for_non_member_names(EnumBaseWithSubclassesDefined): +def test_getattr_raises_attribute_error_for_non_member_names( + EnumBaseWithSubclassesDefined, +): with pytest.raises(AttributeError): EnumBaseWithSubclassesDefined.foobar # pylint:disable=pointless-statement -def test_getattr_does_get_arbitrary_attributes_from_itself(EnumBaseWithSubclassesDefined): +def test_getattr_does_get_arbitrary_attributes_from_itself( + EnumBaseWithSubclassesDefined, +): EnumBase = EnumBaseWithSubclassesDefined EnumBase.foobar = 'foobar' assert EnumBase.foobar == 'foobar' -def test_getattr_does_not_get_arbitrary_attributes_from_subclasses(EnumBaseWithSubclassesDefined, Enum1): +def test_getattr_does_not_get_arbitrary_attributes_from_subclasses( + EnumBaseWithSubclassesDefined, Enum1 +): Enum1.foobar = 'foobar' with pytest.raises(AttributeError): EnumBaseWithSubclassesDefined.foobar # pylint:disable=pointless-statement diff --git a/test/unit/util/test_log.py b/test/unit/util/test_log.py index 91b2d7f1e..aedd87df1 100644 --- a/test/unit/util/test_log.py +++ b/test/unit/util/test_log.py @@ -6,7 +6,6 @@ import boxsdk.util.log - _MOCK_FILEPATH = '/home/user/boxsdk.log' _MOCK_LOG_NAME = 'boxsdk' @@ -42,7 +41,9 @@ def test_setup_logging(stream_or_file, debug, expected_log_level, name, mock_log with patch('logging.getLogger') as get_logger: with patch('logging.open', mock_file_open, create=True): get_logger.return_value = mock_logger - boxsdk.util.log.Logging().setup_logging(stream_or_file, debug=debug, name=name) + boxsdk.util.log.Logging().setup_logging( + stream_or_file, debug=debug, name=name + ) get_logger.assert_called_once_with(name) assert mock_logger.addHandler.call_count == 1 @@ -51,7 +52,10 @@ def test_setup_logging(stream_or_file, debug, expected_log_level, name, mock_log if isinstance(stream_or_file, str): assert mock_file_open.call_count == 1 - assert mock_file_open.call_args[0][:2] == (stream_or_file, 'a') # Python 3 passes additional args. + assert mock_file_open.call_args[0][:2] == ( + stream_or_file, + 'a', + ) # Python 3 passes additional args. def test_setup_logging_is_reentrant(mock_logger): @@ -112,13 +116,17 @@ def test_setup_logging_is_reentrant(mock_logger): {'https': 'http://username:password@localhost:8080'}, {'https': 'http://---:---@localhost:8080'}, ), - ] + ], ) -def test_sanitize_dictionary_correctly_sanitizes_params(mock_logger, unsanitized_dict, expected_result): +def test_sanitize_dictionary_correctly_sanitizes_params( + mock_logger, unsanitized_dict, expected_result +): mock_file_open = mock_open() with patch('logging.getLogger') as get_logger: with patch('logging.open', mock_file_open, create=True): get_logger.return_value = mock_logger - actual_result = boxsdk.util.log.Logging().sanitize_dictionary(unsanitized_dict) + actual_result = boxsdk.util.log.Logging().sanitize_dictionary( + unsanitized_dict + ) assert actual_result == expected_result diff --git a/test/unit/util/test_multipart_stream.py b/test/unit/util/test_multipart_stream.py index 9200a6ad5..d872fb1c5 100644 --- a/test/unit/util/test_multipart_stream.py +++ b/test/unit/util/test_multipart_stream.py @@ -13,14 +13,20 @@ def multipart_stream_files(request): return request.param -def test_multipart_stream_orders_data_before_files(multipart_stream_data, multipart_stream_files): +def test_multipart_stream_orders_data_before_files( + multipart_stream_data, multipart_stream_files +): # pylint:disable=redefined-outer-name if not multipart_stream_data and not multipart_stream_files: pytest.xfail('Encoder does not support empty fields.') stream = MultipartStream(multipart_stream_data, multipart_stream_files) encoded_stream = stream.to_string() - data_indices = [encoded_stream.find(value) for value in multipart_stream_data.values()] - file_indices = [encoded_stream.find(value) for value in multipart_stream_files.values()] + data_indices = [ + encoded_stream.find(value) for value in multipart_stream_data.values() + ] + file_indices = [ + encoded_stream.find(value) for value in multipart_stream_files.values() + ] assert -1 not in data_indices assert -1 not in file_indices assert all(all(data_index < f for f in file_indices) for data_index in data_indices) diff --git a/test/unit/util/test_shared_link.py b/test/unit/util/test_shared_link.py index 84ec5c3c0..88db9678d 100644 --- a/test/unit/util/test_shared_link.py +++ b/test/unit/util/test_shared_link.py @@ -2,7 +2,12 @@ from boxsdk.util.shared_link import get_shared_link_header -@pytest.fixture(params=('mock_shared_link', 'https://app.box.com/s/q2i1024dvguiads6mzj2avsq9hmz43du')) +@pytest.fixture( + params=( + 'mock_shared_link', + 'https://app.box.com/s/q2i1024dvguiads6mzj2avsq9hmz43du', + ) +) def shared_link(request): return request.param diff --git a/test/unit/util/test_text_enum.py b/test/unit/util/test_text_enum.py index d2a180a92..ad1392a23 100644 --- a/test/unit/util/test_text_enum.py +++ b/test/unit/util/test_text_enum.py @@ -6,7 +6,9 @@ class MockTextEnum(TextEnum): def test_text_enum_repr_is_value(): - assert MockTextEnum.MEMBER.__repr__() == MockTextEnum.MEMBER.value # pylint:disable=no-member + assert ( + MockTextEnum.MEMBER.__repr__() == MockTextEnum.MEMBER.value + ) # pylint:disable=no-member def test_text_enum_str_is_value(): diff --git a/test/unit/util/test_translator.py b/test/unit/util/test_translator.py index 5896c6042..51e490109 100644 --- a/test/unit/util/test_translator.py +++ b/test/unit/util/test_translator.py @@ -10,7 +10,6 @@ from boxsdk.object.web_link import WebLink from boxsdk.util.translator import Translator - _response_to_class_mapping = {} @@ -34,13 +33,13 @@ def box_note_response(make_mock_box_request, mock_object_id): @pytest.fixture(autouse=True) def translator_response( - bookmark_response, - box_note_response, - mock_file_response, - mock_folder_response, - mock_group_response, - mock_user_response, - mock_web_link_response, + bookmark_response, + box_note_response, + mock_file_response, + mock_folder_response, + mock_group_response, + mock_user_response, + mock_web_link_response, ): # pylint:disable=redefined-outer-name _response_to_class_mapping['bookmark'] = (bookmark_response, BaseObject) @@ -58,28 +57,39 @@ def metadata_template_response(request): 'type': 'metadata_template', } if request.param in ('scope', 'both'): - response.update({ - 'scope': 'enterprise', - 'templateKey': 'vContract', - }) + response.update( + { + 'scope': 'enterprise', + 'templateKey': 'vContract', + } + ) if request.param in ('id', 'both'): response['id'] = '2f2e84e9-afdb-4e9d-b293-d6d1d932fc85' return response -@pytest.mark.parametrize('response_type', ['bookmark', 'box_note', 'file', 'folder', 'group', 'user']) +@pytest.mark.parametrize( + 'response_type', ['bookmark', 'box_note', 'file', 'folder', 'group', 'user'] +) def test_translator_converts_response_to_correct_type(response_type): response, object_class = _response_to_class_mapping[response_type] assert type(Translator().get(response.json()['type']) == object_class) def test_default_translator(): - assert isinstance(Translator._default_translator, Translator) # pylint:disable=protected-access + assert isinstance( + Translator._default_translator, Translator + ) # pylint:disable=protected-access -@pytest.mark.parametrize(('extend_default_translator', 'new_child'), list(product([None, True], [False, True]))) -def test_with_extend_default_translator(default_translator, extend_default_translator, new_child): +@pytest.mark.parametrize( + ('extend_default_translator', 'new_child'), + list(product([None, True], [False, True])), +) +def test_with_extend_default_translator( + default_translator, extend_default_translator, new_child +): item_type = 'foo' class Foo: @@ -101,11 +111,16 @@ class Foo: mapping = {item_type: Foo} - translator = Translator(mapping, extend_default_translator=False, new_child=new_child) + translator = Translator( + mapping, extend_default_translator=False, new_child=new_child + ) assert translator == mapping -@pytest.mark.parametrize(('new_child', 'extend_default_translator'), list(product([None, True], [False, True]))) +@pytest.mark.parametrize( + ('new_child', 'extend_default_translator'), + list(product([None, True], [False, True])), +) def test_with_new_child(new_child, extend_default_translator): item_type = 'foo' @@ -118,7 +133,9 @@ class Foo: if new_child is not None: kwargs['new_child'] = new_child - translator = Translator(mapping, extend_default_translator=extend_default_translator, **kwargs) + translator = Translator( + mapping, extend_default_translator=extend_default_translator, **kwargs + ) assert item_type in translator assert not translator.maps[0] with pytest.raises(KeyError): @@ -141,7 +158,9 @@ class Foo: mapping = {item_type: Foo} - translator = Translator(mapping, new_child=False, extend_default_translator=extend_default_translator) + translator = Translator( + mapping, new_child=False, extend_default_translator=extend_default_translator + ) assert item_type in translator assert translator.maps[0] is mapping @@ -182,7 +201,9 @@ def test_translate(default_translator, mock_box_session): ], } - results = default_translator.translate(mock_box_session, response_object=response_object) + results = default_translator.translate( + mock_box_session, response_object=response_object + ) test_folder = results['entries'][0] test_file = results['entries'][1] @@ -209,16 +230,24 @@ def test_translate(default_translator, mock_box_session): assert isinstance(response_object['entries'][1], dict) -def test_translator_translates_metadata_template(default_translator, mock_box_session, metadata_template_response): - metadata_template = default_translator.translate(mock_box_session, metadata_template_response) +def test_translator_translates_metadata_template( + default_translator, mock_box_session, metadata_template_response +): + metadata_template = default_translator.translate( + mock_box_session, metadata_template_response + ) if metadata_template_response.get('id'): assert metadata_template.id == metadata_template_response['id'] assert metadata_template.object_id == metadata_template_response['id'] - if metadata_template_response.get('scope') and metadata_template_response.get('templateKey'): + if metadata_template_response.get('scope') and metadata_template_response.get( + 'templateKey' + ): assert metadata_template.scope == metadata_template_response['scope'] - assert metadata_template.template_key == metadata_template_response['templateKey'] + assert ( + metadata_template.template_key == metadata_template_response['templateKey'] + ) if metadata_template_response.get('id') is None: assert metadata_template.object_id is None diff --git a/test/util/streamable_mock_open.py b/test/util/streamable_mock_open.py index d7d018089..cf0a3a3b7 100644 --- a/test/util/streamable_mock_open.py +++ b/test/util/streamable_mock_open.py @@ -14,7 +14,7 @@ def read(size=-1): handle.position = len(read_data) return read_data - data = read_data[handle.position:handle.position + size] + data = read_data[handle.position : handle.position + size] handle.position += size return data