wxcli 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wxcli/__init__.py +4 -0
- wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
- wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
- wxcli/_playbook/.claude/rules/cleanup.md +29 -0
- wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
- wxcli/_playbook/.claude/rules/org-health.md +14 -0
- wxcli/_playbook/.claude/settings.json +8 -0
- wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
- wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
- wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
- wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
- wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
- wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
- wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
- wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
- wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
- wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
- wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
- wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
- wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
- wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
- wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
- wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
- wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
- wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
- wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
- wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
- wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
- wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
- wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
- wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
- wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
- wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
- wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
- wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
- wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
- wxcli/_playbook/CLAUDE.md +373 -0
- wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
- wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
- wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
- wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
- wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
- wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
- wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
- wxcli/_playbook/docs/reference/admin-partner.md +357 -0
- wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
- wxcli/_playbook/docs/reference/authentication.md +965 -0
- wxcli/_playbook/docs/reference/call-control.md +1205 -0
- wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
- wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
- wxcli/_playbook/docs/reference/call-routing.md +2304 -0
- wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
- wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
- wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
- wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
- wxcli/_playbook/docs/reference/devices-core.md +1869 -0
- wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
- wxcli/_playbook/docs/reference/devices-platform.md +467 -0
- wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
- wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
- wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
- wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
- wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
- wxcli/_playbook/docs/reference/meetings-content.md +473 -0
- wxcli/_playbook/docs/reference/meetings-core.md +903 -0
- wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
- wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
- wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
- wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
- wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
- wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
- wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
- wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
- wxcli/_playbook/docs/reference/provisioning.md +1366 -0
- wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
- wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
- wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
- wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
- wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
- wxcli/_version.py +24 -0
- wxcli/auth.py +137 -0
- wxcli/commands/__init__.py +0 -0
- wxcli/commands/_registry.py +181 -0
- wxcli/commands/activation_email.py +103 -0
- wxcli/commands/admin_recordings.py +585 -0
- wxcli/commands/analytics.py +105 -0
- wxcli/commands/announcement_playlists.py +219 -0
- wxcli/commands/announcements.py +519 -0
- wxcli/commands/archive_users.py +36 -0
- wxcli/commands/attachment_actions.py +60 -0
- wxcli/commands/audit_events.py +83 -0
- wxcli/commands/authorizations.py +113 -0
- wxcli/commands/auto_attendant.py +642 -0
- wxcli/commands/broadworks_billing_reports.py +126 -0
- wxcli/commands/broadworks_enterprises.py +160 -0
- wxcli/commands/broadworks_subscribers.py +319 -0
- wxcli/commands/broadworks_workspaces.py +108 -0
- wxcli/commands/call_controls.py +1304 -0
- wxcli/commands/call_park.py +494 -0
- wxcli/commands/call_pickup.py +229 -0
- wxcli/commands/call_queue.py +1878 -0
- wxcli/commands/call_recording.py +707 -0
- wxcli/commands/call_routing.py +1610 -0
- wxcli/commands/call_settings_for_me_phase_5.py +212 -0
- wxcli/commands/caller_reputation.py +169 -0
- wxcli/commands/calling_service.py +305 -0
- wxcli/commands/cc_address_book.py +770 -0
- wxcli/commands/cc_agent_greetings.py +242 -0
- wxcli/commands/cc_agent_summaries.py +39 -0
- wxcli/commands/cc_agent_wellbeing.py +216 -0
- wxcli/commands/cc_agents.py +425 -0
- wxcli/commands/cc_ai_assistant.py +55 -0
- wxcli/commands/cc_ai_feature.py +294 -0
- wxcli/commands/cc_audio_files.py +149 -0
- wxcli/commands/cc_auto_csat.py +311 -0
- wxcli/commands/cc_aux_code.py +458 -0
- wxcli/commands/cc_business_hour.py +353 -0
- wxcli/commands/cc_call_monitoring.py +228 -0
- wxcli/commands/cc_callbacks.py +194 -0
- wxcli/commands/cc_campaign.py +212 -0
- wxcli/commands/cc_captures.py +39 -0
- wxcli/commands/cc_contact_list.py +169 -0
- wxcli/commands/cc_contact_number.py +295 -0
- wxcli/commands/cc_data_sources.py +235 -0
- wxcli/commands/cc_desktop_layout.py +380 -0
- wxcli/commands/cc_desktop_profile.py +322 -0
- wxcli/commands/cc_dial_number.py +511 -0
- wxcli/commands/cc_dial_plan.py +347 -0
- wxcli/commands/cc_dnc.py +101 -0
- wxcli/commands/cc_entry_point.py +545 -0
- wxcli/commands/cc_ewt.py +51 -0
- wxcli/commands/cc_flow.py +477 -0
- wxcli/commands/cc_global_vars.py +430 -0
- wxcli/commands/cc_holiday_list.py +335 -0
- wxcli/commands/cc_journey.py +1261 -0
- wxcli/commands/cc_legacy_flows.py +49 -0
- wxcli/commands/cc_multimedia_profile.py +436 -0
- wxcli/commands/cc_notification.py +51 -0
- wxcli/commands/cc_outdial_ani.py +604 -0
- wxcli/commands/cc_overrides.py +341 -0
- wxcli/commands/cc_queue.py +1083 -0
- wxcli/commands/cc_queue_stats.py +55 -0
- wxcli/commands/cc_realtime.py +51 -0
- wxcli/commands/cc_resource_collection.py +312 -0
- wxcli/commands/cc_search.py +46 -0
- wxcli/commands/cc_site.py +400 -0
- wxcli/commands/cc_skill.py +350 -0
- wxcli/commands/cc_skill_profile.py +289 -0
- wxcli/commands/cc_subscriptions.py +426 -0
- wxcli/commands/cc_summaries.py +130 -0
- wxcli/commands/cc_tasks.py +844 -0
- wxcli/commands/cc_team.py +325 -0
- wxcli/commands/cc_user_profiles.py +349 -0
- wxcli/commands/cc_users.py +701 -0
- wxcli/commands/cc_work_types.py +363 -0
- wxcli/commands/cdr.py +89 -0
- wxcli/commands/classifications.py +38 -0
- wxcli/commands/cleanup.py +1427 -0
- wxcli/commands/client_settings.py +72 -0
- wxcli/commands/conference.py +328 -0
- wxcli/commands/configure.py +72 -0
- wxcli/commands/converged_recordings.py +347 -0
- wxcli/commands/converged_recordings_export.py +310 -0
- wxcli/commands/cq_playlists.py +42 -0
- wxcli/commands/cucm.py +2794 -0
- wxcli/commands/cucm_config.py +129 -0
- wxcli/commands/customer_assist.py +373 -0
- wxcli/commands/data_sources.py +227 -0
- wxcli/commands/dect_devices.py +787 -0
- wxcli/commands/device_configurations.py +77 -0
- wxcli/commands/device_dynamic_settings.py +320 -0
- wxcli/commands/device_settings.py +1489 -0
- wxcli/commands/devices.py +280 -0
- wxcli/commands/domains.py +142 -0
- wxcli/commands/ecm.py +178 -0
- wxcli/commands/emergency_services.py +827 -0
- wxcli/commands/events.py +83 -0
- wxcli/commands/external_voicemail.py +52 -0
- wxcli/commands/groups.py +206 -0
- wxcli/commands/guest_management.py +76 -0
- wxcli/commands/hds.py +340 -0
- wxcli/commands/hot_desk.py +67 -0
- wxcli/commands/hot_desking_members.py +118 -0
- wxcli/commands/hot_desking_portal.py +124 -0
- wxcli/commands/hunt_group.py +577 -0
- wxcli/commands/hybrid_clusters.py +71 -0
- wxcli/commands/hybrid_connectors.py +67 -0
- wxcli/commands/identity_org.py +159 -0
- wxcli/commands/init_playbook.py +155 -0
- wxcli/commands/licenses.py +106 -0
- wxcli/commands/live_monitoring.py +40 -0
- wxcli/commands/location_call_handling.py +577 -0
- wxcli/commands/location_schedules.py +341 -0
- wxcli/commands/location_settings.py +1297 -0
- wxcli/commands/location_voicemail.py +494 -0
- wxcli/commands/locations.py +336 -0
- wxcli/commands/meeting_captions.py +110 -0
- wxcli/commands/meeting_chats.py +63 -0
- wxcli/commands/meeting_invitees.py +234 -0
- wxcli/commands/meeting_messages.py +28 -0
- wxcli/commands/meeting_participants.py +278 -0
- wxcli/commands/meeting_polls.py +111 -0
- wxcli/commands/meeting_preferences.py +515 -0
- wxcli/commands/meeting_qa.py +78 -0
- wxcli/commands/meeting_qualities.py +43 -0
- wxcli/commands/meeting_reports.py +101 -0
- wxcli/commands/meeting_session_types.py +105 -0
- wxcli/commands/meeting_site.py +57 -0
- wxcli/commands/meeting_slido.py +44 -0
- wxcli/commands/meeting_summaries.py +92 -0
- wxcli/commands/meeting_tracking_codes.py +233 -0
- wxcli/commands/meeting_transcripts.py +230 -0
- wxcli/commands/meetings.py +1997 -0
- wxcli/commands/memberships.py +164 -0
- wxcli/commands/messages.py +213 -0
- wxcli/commands/mode_management.py +244 -0
- wxcli/commands/my_call_settings.py +3577 -0
- wxcli/commands/numbers.py +405 -0
- wxcli/commands/operating_modes.py +410 -0
- wxcli/commands/org_contacts.py +284 -0
- wxcli/commands/org_health_cli.py +55 -0
- wxcli/commands/org_settings.py +71 -0
- wxcli/commands/organizations.py +83 -0
- wxcli/commands/paging_group.py +257 -0
- wxcli/commands/partner_admins.py +141 -0
- wxcli/commands/partner_reports.py +188 -0
- wxcli/commands/partner_tags.py +216 -0
- wxcli/commands/people.py +279 -0
- wxcli/commands/person_call_settings.py +71 -0
- wxcli/commands/pstn.py +280 -0
- wxcli/commands/recording_report.py +154 -0
- wxcli/commands/report_templates.py +38 -0
- wxcli/commands/reports.py +144 -0
- wxcli/commands/resource_group_memberships.py +160 -0
- wxcli/commands/resource_groups.py +67 -0
- wxcli/commands/roles.py +63 -0
- wxcli/commands/room_tabs.py +160 -0
- wxcli/commands/rooms.py +225 -0
- wxcli/commands/scim_bulk.py +46 -0
- wxcli/commands/scim_groups.py +246 -0
- wxcli/commands/scim_schemas.py +82 -0
- wxcli/commands/scim_users.py +283 -0
- wxcli/commands/security_audit.py +54 -0
- wxcli/commands/service_apps.py +51 -0
- wxcli/commands/single_number_reach.py +209 -0
- wxcli/commands/team_memberships.py +155 -0
- wxcli/commands/teams.py +153 -0
- wxcli/commands/ucm_profile.py +42 -0
- wxcli/commands/update.py +219 -0
- wxcli/commands/user_settings.py +5116 -0
- wxcli/commands/video_mesh.py +1090 -0
- wxcli/commands/virtual_extensions.py +502 -0
- wxcli/commands/virtual_line_settings.py +2072 -0
- wxcli/commands/webhooks.py +176 -0
- wxcli/commands/wholesale_billing_reports.py +147 -0
- wxcli/commands/wholesale_provisioning.py +523 -0
- wxcli/commands/workspace_locations.py +339 -0
- wxcli/commands/workspace_metrics.py +103 -0
- wxcli/commands/workspace_personalization.py +70 -0
- wxcli/commands/workspace_settings.py +3347 -0
- wxcli/commands/workspaces.py +279 -0
- wxcli/commands/xapi.py +114 -0
- wxcli/config.py +139 -0
- wxcli/errors.py +77 -0
- wxcli/main.py +210 -0
- wxcli/migration/CLAUDE.md +106 -0
- wxcli/migration/__init__.py +1 -0
- wxcli/migration/advisory/CLAUDE.md +196 -0
- wxcli/migration/advisory/__init__.py +46 -0
- wxcli/migration/advisory/advisor.py +107 -0
- wxcli/migration/advisory/advisory_patterns.py +2552 -0
- wxcli/migration/advisory/recommendation_rules.py +835 -0
- wxcli/migration/cucm/CLAUDE.md +74 -0
- wxcli/migration/cucm/__init__.py +5 -0
- wxcli/migration/cucm/connection.py +265 -0
- wxcli/migration/cucm/discovery.py +225 -0
- wxcli/migration/cucm/extractors/__init__.py +1 -0
- wxcli/migration/cucm/extractors/announcements.py +92 -0
- wxcli/migration/cucm/extractors/base.py +90 -0
- wxcli/migration/cucm/extractors/device_profiles.py +98 -0
- wxcli/migration/cucm/extractors/devices.py +193 -0
- wxcli/migration/cucm/extractors/e911.py +103 -0
- wxcli/migration/cucm/extractors/features.py +309 -0
- wxcli/migration/cucm/extractors/helpers.py +73 -0
- wxcli/migration/cucm/extractors/informational.py +295 -0
- wxcli/migration/cucm/extractors/locations.py +221 -0
- wxcli/migration/cucm/extractors/moh.py +100 -0
- wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
- wxcli/migration/cucm/extractors/routing.py +493 -0
- wxcli/migration/cucm/extractors/shared_lines.py +146 -0
- wxcli/migration/cucm/extractors/templates.py +229 -0
- wxcli/migration/cucm/extractors/tier4.py +173 -0
- wxcli/migration/cucm/extractors/users.py +240 -0
- wxcli/migration/cucm/extractors/voicemail.py +243 -0
- wxcli/migration/cucm/extractors/workspaces.py +88 -0
- wxcli/migration/cucm/unity_connection.py +304 -0
- wxcli/migration/execute/CLAUDE.md +372 -0
- wxcli/migration/execute/__init__.py +303 -0
- wxcli/migration/execute/batch.py +277 -0
- wxcli/migration/execute/dependency.py +483 -0
- wxcli/migration/execute/engine.py +964 -0
- wxcli/migration/execute/handlers.py +2232 -0
- wxcli/migration/execute/planner.py +1939 -0
- wxcli/migration/execute/runtime.py +571 -0
- wxcli/migration/export/CLAUDE.md +49 -0
- wxcli/migration/export/__init__.py +8 -0
- wxcli/migration/export/csv_export.py +126 -0
- wxcli/migration/export/deployment_plan.py +479 -0
- wxcli/migration/export/json_export.py +60 -0
- wxcli/migration/models.py +941 -0
- wxcli/migration/phone_models.py +50 -0
- wxcli/migration/preflight/CLAUDE.md +52 -0
- wxcli/migration/preflight/__init__.py +89 -0
- wxcli/migration/preflight/checks.py +911 -0
- wxcli/migration/preflight/runner.py +293 -0
- wxcli/migration/rate_limiter.py +144 -0
- wxcli/migration/report/CLAUDE.md +245 -0
- wxcli/migration/report/__init__.py +7 -0
- wxcli/migration/report/appendix.py +2437 -0
- wxcli/migration/report/assembler.py +183 -0
- wxcli/migration/report/charts.py +263 -0
- wxcli/migration/report/executive.py +776 -0
- wxcli/migration/report/explainer.py +704 -0
- wxcli/migration/report/helpers.py +74 -0
- wxcli/migration/report/ingest.py +152 -0
- wxcli/migration/report/notice_templates.py +192 -0
- wxcli/migration/report/score.py +375 -0
- wxcli/migration/report/styles.py +1052 -0
- wxcli/migration/report/user_diff.py +1052 -0
- wxcli/migration/report/user_notice.py +425 -0
- wxcli/migration/state.py +139 -0
- wxcli/migration/store.py +860 -0
- wxcli/migration/transform/CLAUDE.md +107 -0
- wxcli/migration/transform/__init__.py +12 -0
- wxcli/migration/transform/analysis_pipeline.py +453 -0
- wxcli/migration/transform/analyzers/__init__.py +142 -0
- wxcli/migration/transform/analyzers/css_permission.py +189 -0
- wxcli/migration/transform/analyzers/css_routing.py +335 -0
- wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
- wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
- wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
- wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
- wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
- wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
- wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
- wxcli/migration/transform/analyzers/missing_data.py +300 -0
- wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
- wxcli/migration/transform/analyzers/shared_line.py +170 -0
- wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
- wxcli/migration/transform/analyzers/workspace_license.py +212 -0
- wxcli/migration/transform/cross_reference.py +1136 -0
- wxcli/migration/transform/cucm_pattern.py +246 -0
- wxcli/migration/transform/decisions.py +301 -0
- wxcli/migration/transform/e164.py +145 -0
- wxcli/migration/transform/engine.py +256 -0
- wxcli/migration/transform/mappers/CLAUDE.md +123 -0
- wxcli/migration/transform/mappers/__init__.py +76 -0
- wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
- wxcli/migration/transform/mappers/base.py +190 -0
- wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
- wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
- wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
- wxcli/migration/transform/mappers/css_mapper.py +868 -0
- wxcli/migration/transform/mappers/dect_mapper.py +367 -0
- wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
- wxcli/migration/transform/mappers/device_mapper.py +227 -0
- wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
- wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
- wxcli/migration/transform/mappers/e911_mapper.py +129 -0
- wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
- wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
- wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
- wxcli/migration/transform/mappers/line_mapper.py +310 -0
- wxcli/migration/transform/mappers/location_mapper.py +235 -0
- wxcli/migration/transform/mappers/moh_mapper.py +114 -0
- wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
- wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
- wxcli/migration/transform/mappers/routing_mapper.py +821 -0
- wxcli/migration/transform/mappers/snr_mapper.py +201 -0
- wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
- wxcli/migration/transform/mappers/user_mapper.py +264 -0
- wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
- wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
- wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
- wxcli/migration/transform/normalizers.py +2008 -0
- wxcli/migration/transform/pattern_converter.py +106 -0
- wxcli/migration/transform/pipeline.py +271 -0
- wxcli/migration/transform/rules.py +255 -0
- wxcli/org_health/CLAUDE.md +63 -0
- wxcli/org_health/__init__.py +7 -0
- wxcli/org_health/analyze.py +93 -0
- wxcli/org_health/checks.py +458 -0
- wxcli/org_health/collector.py +62 -0
- wxcli/org_health/models.py +78 -0
- wxcli/org_health/report.py +300 -0
- wxcli/output.py +101 -0
- wxcli-1.2.0.dist-info/METADATA +505 -0
- wxcli-1.2.0.dist-info/RECORD +413 -0
- wxcli-1.2.0.dist-info/WHEEL +5 -0
- wxcli-1.2.0.dist-info/entry_points.txt +2 -0
- wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
- wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
- wxcli-1.2.0.dist-info/scm_version.json +8 -0
- wxcli-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from wxcli.auth import get_api
|
|
4
|
+
from wxcli.errors import WebexError, handle_rest_error
|
|
5
|
+
from wxcli.output import print_table, print_json
|
|
6
|
+
from wxcli.config import get_org_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage Webex Calling resource-group-memberships.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
license_id: str = typer.Option(None, "--license-id", help="List resource group memberships for a license, by ID."),
|
|
15
|
+
person_id: str = typer.Option(None, "--person-id", help="List resource group memberships for a person, by ID."),
|
|
16
|
+
person_org_id: str = typer.Option(None, "--person-org-id", help="List resource group memberships for an organization, by ID."),
|
|
17
|
+
status: str = typer.Option(None, "--status", help="Choices: pending, activated, error"),
|
|
18
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
19
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
20
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
21
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
22
|
+
):
|
|
23
|
+
"""List Resource Group Memberships."""
|
|
24
|
+
api = get_api(debug=debug)
|
|
25
|
+
url = f"https://webexapis.com/v1/resourceGroup/memberships"
|
|
26
|
+
params = {}
|
|
27
|
+
if license_id is not None:
|
|
28
|
+
params["licenseId"] = license_id
|
|
29
|
+
if person_id is not None:
|
|
30
|
+
params["personId"] = person_id
|
|
31
|
+
if person_org_id is not None:
|
|
32
|
+
params["personOrgId"] = person_org_id
|
|
33
|
+
if status is not None:
|
|
34
|
+
params["status"] = status
|
|
35
|
+
if limit > 0:
|
|
36
|
+
params["max"] = limit
|
|
37
|
+
if offset > 0:
|
|
38
|
+
params["start"] = offset
|
|
39
|
+
try:
|
|
40
|
+
if limit > 0:
|
|
41
|
+
result = api.session.rest_get(url, params=params)
|
|
42
|
+
result = result or {}
|
|
43
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
44
|
+
else:
|
|
45
|
+
items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
|
|
46
|
+
except WebexError as e:
|
|
47
|
+
handle_rest_error(e)
|
|
48
|
+
if output == "json":
|
|
49
|
+
print_json(items)
|
|
50
|
+
else:
|
|
51
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command("list-v2")
|
|
56
|
+
def list_v2(
|
|
57
|
+
license_id: str = typer.Option(None, "--license-id", help="List resource group memberships for a license, by ID."),
|
|
58
|
+
id_param: str = typer.Option(None, "--id", help="List resource group memberships by ID."),
|
|
59
|
+
status: str = typer.Option(None, "--status", help="Choices: pending, activated, error"),
|
|
60
|
+
type_param: str = typer.Option(None, "--type", help="Choices: User, Workspace"),
|
|
61
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
62
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
63
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
64
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
65
|
+
):
|
|
66
|
+
"""List Resource Group Memberships V2."""
|
|
67
|
+
api = get_api(debug=debug)
|
|
68
|
+
url = f"https://webexapis.com/v1/resourceGroup/memberships/v2"
|
|
69
|
+
params = {}
|
|
70
|
+
if license_id is not None:
|
|
71
|
+
params["licenseId"] = license_id
|
|
72
|
+
if id_param is not None:
|
|
73
|
+
params["id"] = id_param
|
|
74
|
+
if status is not None:
|
|
75
|
+
params["status"] = status
|
|
76
|
+
if type_param is not None:
|
|
77
|
+
params["type"] = type_param
|
|
78
|
+
if limit > 0:
|
|
79
|
+
params["max"] = limit
|
|
80
|
+
if offset > 0:
|
|
81
|
+
params["start"] = offset
|
|
82
|
+
org_id = get_org_id()
|
|
83
|
+
if org_id is not None:
|
|
84
|
+
params["orgId"] = org_id
|
|
85
|
+
try:
|
|
86
|
+
if limit > 0:
|
|
87
|
+
result = api.session.rest_get(url, params=params)
|
|
88
|
+
result = result or {}
|
|
89
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
90
|
+
else:
|
|
91
|
+
items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
|
|
92
|
+
except WebexError as e:
|
|
93
|
+
handle_rest_error(e)
|
|
94
|
+
if output == "json":
|
|
95
|
+
print_json(items)
|
|
96
|
+
else:
|
|
97
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@app.command("show")
|
|
102
|
+
def show(
|
|
103
|
+
resource_group_membership_id: str = typer.Argument(help="resourceGroupMembershipId"),
|
|
104
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
105
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
106
|
+
):
|
|
107
|
+
"""Get Resource Group Membership Details."""
|
|
108
|
+
api = get_api(debug=debug)
|
|
109
|
+
url = f"https://webexapis.com/v1/resourceGroup/memberships/{resource_group_membership_id}"
|
|
110
|
+
try:
|
|
111
|
+
result = api.session.rest_get(url)
|
|
112
|
+
except WebexError as e:
|
|
113
|
+
handle_rest_error(e)
|
|
114
|
+
if output == "json":
|
|
115
|
+
print_json(result)
|
|
116
|
+
else:
|
|
117
|
+
if isinstance(result, dict):
|
|
118
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
119
|
+
elif isinstance(result, list):
|
|
120
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
121
|
+
else:
|
|
122
|
+
print_json(result)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.command("update")
|
|
127
|
+
def update(
|
|
128
|
+
resource_group_membership_id: str = typer.Argument(help="resourceGroupMembershipId"),
|
|
129
|
+
resource_group_id: str = typer.Option(None, "--resource-group-id", help="The resource group ID."),
|
|
130
|
+
license_id: str = typer.Option(None, "--license-id", help="The license ID."),
|
|
131
|
+
person_id: str = typer.Option(None, "--person-id", help="The person ID."),
|
|
132
|
+
person_org_id: str = typer.Option(None, "--person-org-id", help="The organization ID of the person."),
|
|
133
|
+
status: str = typer.Option(None, "--status", help="Choices: pending, activated, error"),
|
|
134
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
135
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
136
|
+
):
|
|
137
|
+
"""Update a Resource Group Membership."""
|
|
138
|
+
api = get_api(debug=debug)
|
|
139
|
+
url = f"https://webexapis.com/v1/resourceGroup/memberships/{resource_group_membership_id}"
|
|
140
|
+
if json_body:
|
|
141
|
+
body = json.loads(json_body)
|
|
142
|
+
else:
|
|
143
|
+
body = {}
|
|
144
|
+
if resource_group_id is not None:
|
|
145
|
+
body["resourceGroupId"] = resource_group_id
|
|
146
|
+
if license_id is not None:
|
|
147
|
+
body["licenseId"] = license_id
|
|
148
|
+
if person_id is not None:
|
|
149
|
+
body["personId"] = person_id
|
|
150
|
+
if person_org_id is not None:
|
|
151
|
+
body["personOrgId"] = person_org_id
|
|
152
|
+
if status is not None:
|
|
153
|
+
body["status"] = status
|
|
154
|
+
try:
|
|
155
|
+
result = api.session.rest_put(url, json=body)
|
|
156
|
+
except WebexError as e:
|
|
157
|
+
handle_rest_error(e)
|
|
158
|
+
typer.echo(f"Updated.")
|
|
159
|
+
|
|
160
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from wxcli.auth import get_api
|
|
4
|
+
from wxcli.errors import WebexError, handle_rest_error
|
|
5
|
+
from wxcli.output import print_table, print_json
|
|
6
|
+
from wxcli.config import get_org_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage Webex Calling resource-groups.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
15
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
16
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
17
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
18
|
+
):
|
|
19
|
+
"""List Resource Groups."""
|
|
20
|
+
api = get_api(debug=debug)
|
|
21
|
+
url = f"https://webexapis.com/v1/resourceGroups"
|
|
22
|
+
params = {}
|
|
23
|
+
if limit > 0:
|
|
24
|
+
params["max"] = limit
|
|
25
|
+
if offset > 0:
|
|
26
|
+
params["start"] = offset
|
|
27
|
+
org_id = get_org_id()
|
|
28
|
+
if org_id is not None:
|
|
29
|
+
params["orgId"] = org_id
|
|
30
|
+
result = None
|
|
31
|
+
try:
|
|
32
|
+
result = api.session.rest_get(url, params=params)
|
|
33
|
+
except WebexError as e:
|
|
34
|
+
handle_rest_error(e)
|
|
35
|
+
result = result or []
|
|
36
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
37
|
+
if output == "json":
|
|
38
|
+
print_json(items)
|
|
39
|
+
else:
|
|
40
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.command("show")
|
|
45
|
+
def show(
|
|
46
|
+
resource_group_id: str = typer.Argument(help="resourceGroupId"),
|
|
47
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
48
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
49
|
+
):
|
|
50
|
+
"""Get Resource Group Details."""
|
|
51
|
+
api = get_api(debug=debug)
|
|
52
|
+
url = f"https://webexapis.com/v1/resourceGroups/{resource_group_id}"
|
|
53
|
+
try:
|
|
54
|
+
result = api.session.rest_get(url)
|
|
55
|
+
except WebexError as e:
|
|
56
|
+
handle_rest_error(e)
|
|
57
|
+
if output == "json":
|
|
58
|
+
print_json(result)
|
|
59
|
+
else:
|
|
60
|
+
if isinstance(result, dict):
|
|
61
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
62
|
+
elif isinstance(result, list):
|
|
63
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
64
|
+
else:
|
|
65
|
+
print_json(result)
|
|
66
|
+
|
|
67
|
+
|
wxcli/commands/roles.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from wxcli.auth import get_api
|
|
4
|
+
from wxcli.errors import WebexError, handle_rest_error
|
|
5
|
+
from wxcli.output import print_table, print_json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(help="Manage Webex Calling roles.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("list")
|
|
12
|
+
def cmd_list(
|
|
13
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
14
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
15
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
16
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
17
|
+
):
|
|
18
|
+
"""List Roles."""
|
|
19
|
+
api = get_api(debug=debug)
|
|
20
|
+
url = f"https://webexapis.com/v1/roles"
|
|
21
|
+
params = {}
|
|
22
|
+
if limit > 0:
|
|
23
|
+
params["max"] = limit
|
|
24
|
+
if offset > 0:
|
|
25
|
+
params["start"] = offset
|
|
26
|
+
result = None
|
|
27
|
+
try:
|
|
28
|
+
result = api.session.rest_get(url, params=params)
|
|
29
|
+
except WebexError as e:
|
|
30
|
+
handle_rest_error(e)
|
|
31
|
+
result = result or []
|
|
32
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
33
|
+
if output == "json":
|
|
34
|
+
print_json(items)
|
|
35
|
+
else:
|
|
36
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name')], limit=limit)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.command("show")
|
|
41
|
+
def show(
|
|
42
|
+
role_id: str = typer.Argument(help="roleId"),
|
|
43
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
44
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
45
|
+
):
|
|
46
|
+
"""Get Role Details."""
|
|
47
|
+
api = get_api(debug=debug)
|
|
48
|
+
url = f"https://webexapis.com/v1/roles/{role_id}"
|
|
49
|
+
try:
|
|
50
|
+
result = api.session.rest_get(url)
|
|
51
|
+
except WebexError as e:
|
|
52
|
+
handle_rest_error(e)
|
|
53
|
+
if output == "json":
|
|
54
|
+
print_json(result)
|
|
55
|
+
else:
|
|
56
|
+
if isinstance(result, dict):
|
|
57
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
58
|
+
elif isinstance(result, list):
|
|
59
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
60
|
+
else:
|
|
61
|
+
print_json(result)
|
|
62
|
+
|
|
63
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from wxcli.auth import get_api
|
|
4
|
+
from wxcli.errors import WebexError, handle_rest_error
|
|
5
|
+
from wxcli.output import print_table, print_json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(help="Manage Webex Calling room-tabs.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("list")
|
|
12
|
+
def cmd_list(
|
|
13
|
+
room_id: str = typer.Option(..., "--room-id", help="ID of the room for which to list room tabs."),
|
|
14
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
15
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
16
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
17
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
18
|
+
):
|
|
19
|
+
"""List Room Tabs."""
|
|
20
|
+
api = get_api(debug=debug)
|
|
21
|
+
url = f"https://webexapis.com/v1/room/tabs"
|
|
22
|
+
params = {}
|
|
23
|
+
if room_id is not None:
|
|
24
|
+
params["roomId"] = room_id
|
|
25
|
+
if limit > 0:
|
|
26
|
+
params["max"] = limit
|
|
27
|
+
if offset > 0:
|
|
28
|
+
params["start"] = offset
|
|
29
|
+
try:
|
|
30
|
+
if limit > 0:
|
|
31
|
+
result = api.session.rest_get(url, params=params)
|
|
32
|
+
result = result or {}
|
|
33
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
34
|
+
else:
|
|
35
|
+
if "max" not in params:
|
|
36
|
+
params["max"] = 1000
|
|
37
|
+
items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
|
|
38
|
+
except WebexError as e:
|
|
39
|
+
handle_rest_error(e)
|
|
40
|
+
if output == "json":
|
|
41
|
+
print_json(items)
|
|
42
|
+
else:
|
|
43
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command("create")
|
|
48
|
+
def create(
|
|
49
|
+
room_id: str = typer.Option(None, "--room-id", help="(required) A unique identifier for the room."),
|
|
50
|
+
content_url: str = typer.Option(None, "--content-url", help="(required) URL of the Room Tab. Must use `https` protocol."),
|
|
51
|
+
display_name: str = typer.Option(None, "--display-name", help="(required) User-friendly name for the room tab."),
|
|
52
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
53
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
54
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
55
|
+
):
|
|
56
|
+
"""Create a Room Tab."""
|
|
57
|
+
api = get_api(debug=debug)
|
|
58
|
+
url = f"https://webexapis.com/v1/room/tabs"
|
|
59
|
+
if json_body:
|
|
60
|
+
body = json.loads(json_body)
|
|
61
|
+
else:
|
|
62
|
+
body = {}
|
|
63
|
+
if room_id is not None:
|
|
64
|
+
body["roomId"] = room_id
|
|
65
|
+
if content_url is not None:
|
|
66
|
+
body["contentUrl"] = content_url
|
|
67
|
+
if display_name is not None:
|
|
68
|
+
body["displayName"] = display_name
|
|
69
|
+
_missing = [f for f in ['roomId', 'contentUrl', 'displayName'] if f not in body or body[f] is None]
|
|
70
|
+
if _missing:
|
|
71
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
72
|
+
raise typer.Exit(1)
|
|
73
|
+
try:
|
|
74
|
+
result = api.session.rest_post(url, json=body)
|
|
75
|
+
except WebexError as e:
|
|
76
|
+
handle_rest_error(e)
|
|
77
|
+
if output == "json":
|
|
78
|
+
print_json(result)
|
|
79
|
+
elif isinstance(result, dict) and "id" in result:
|
|
80
|
+
typer.echo(f"Created: {result['id']}")
|
|
81
|
+
elif not result or result == {}:
|
|
82
|
+
typer.echo("Created.")
|
|
83
|
+
else:
|
|
84
|
+
print_json(result)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@app.command("show")
|
|
89
|
+
def show(
|
|
90
|
+
id: str = typer.Argument(help="id"),
|
|
91
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
92
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
93
|
+
):
|
|
94
|
+
"""Get Room Tab Details."""
|
|
95
|
+
api = get_api(debug=debug)
|
|
96
|
+
url = f"https://webexapis.com/v1/room/tabs/{id}"
|
|
97
|
+
try:
|
|
98
|
+
result = api.session.rest_get(url)
|
|
99
|
+
except WebexError as e:
|
|
100
|
+
handle_rest_error(e)
|
|
101
|
+
if output == "json":
|
|
102
|
+
print_json(result)
|
|
103
|
+
else:
|
|
104
|
+
if isinstance(result, dict):
|
|
105
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
106
|
+
elif isinstance(result, list):
|
|
107
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
108
|
+
else:
|
|
109
|
+
print_json(result)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@app.command("update")
|
|
114
|
+
def update(
|
|
115
|
+
id: str = typer.Argument(help="id"),
|
|
116
|
+
room_id: str = typer.Option(None, "--room-id", help="ID of the room that contains the room tab in question."),
|
|
117
|
+
content_url: str = typer.Option(None, "--content-url", help="Content URL of the Room Tab. URL must use `https` protocol."),
|
|
118
|
+
display_name: str = typer.Option(None, "--display-name", help="User-friendly name for the room tab."),
|
|
119
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
120
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
121
|
+
):
|
|
122
|
+
"""Update a Room Tab."""
|
|
123
|
+
api = get_api(debug=debug)
|
|
124
|
+
url = f"https://webexapis.com/v1/room/tabs/{id}"
|
|
125
|
+
if json_body:
|
|
126
|
+
body = json.loads(json_body)
|
|
127
|
+
else:
|
|
128
|
+
body = {}
|
|
129
|
+
if room_id is not None:
|
|
130
|
+
body["roomId"] = room_id
|
|
131
|
+
if content_url is not None:
|
|
132
|
+
body["contentUrl"] = content_url
|
|
133
|
+
if display_name is not None:
|
|
134
|
+
body["displayName"] = display_name
|
|
135
|
+
try:
|
|
136
|
+
result = api.session.rest_put(url, json=body)
|
|
137
|
+
except WebexError as e:
|
|
138
|
+
handle_rest_error(e)
|
|
139
|
+
typer.echo(f"Updated.")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@app.command("delete")
|
|
144
|
+
def delete(
|
|
145
|
+
id: str = typer.Argument(help="id"),
|
|
146
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
147
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
148
|
+
):
|
|
149
|
+
"""Delete a Room Tab."""
|
|
150
|
+
if not force:
|
|
151
|
+
typer.confirm(f"Delete {id}?", abort=True)
|
|
152
|
+
api = get_api(debug=debug)
|
|
153
|
+
url = f"https://webexapis.com/v1/room/tabs/{id}"
|
|
154
|
+
try:
|
|
155
|
+
api.session.rest_delete(url)
|
|
156
|
+
except WebexError as e:
|
|
157
|
+
handle_rest_error(e)
|
|
158
|
+
typer.echo(f"Deleted: {id}")
|
|
159
|
+
|
|
160
|
+
|
wxcli/commands/rooms.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from wxcli.auth import get_api
|
|
4
|
+
from wxcli.errors import WebexError, handle_rest_error
|
|
5
|
+
from wxcli.output import print_table, print_json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(help="Manage Webex Calling rooms.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("list")
|
|
12
|
+
def cmd_list(
|
|
13
|
+
team_id: str = typer.Option(None, "--team-id", help="List rooms associated with a team, by ID. Cannot be set in c"),
|
|
14
|
+
type_param: str = typer.Option(None, "--type", help="Choices: direct, group"),
|
|
15
|
+
org_public_spaces: str = typer.Option(None, "--org-public-spaces", help="Shows the org's public spaces joined and unjoined. When set"),
|
|
16
|
+
from_param: str = typer.Option(None, "--from", help="Filters rooms, that were made public after this time. See `m"),
|
|
17
|
+
to: str = typer.Option(None, "--to", help="Filters rooms, that were made public before this time. See `"),
|
|
18
|
+
sort_by: str = typer.Option(None, "--sort-by", help="Choices: id, lastactivity, created"),
|
|
19
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
20
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
21
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
22
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
23
|
+
):
|
|
24
|
+
"""List Rooms."""
|
|
25
|
+
api = get_api(debug=debug)
|
|
26
|
+
url = f"https://webexapis.com/v1/rooms"
|
|
27
|
+
params = {}
|
|
28
|
+
if team_id is not None:
|
|
29
|
+
params["teamId"] = team_id
|
|
30
|
+
if type_param is not None:
|
|
31
|
+
params["type"] = type_param
|
|
32
|
+
if org_public_spaces is not None:
|
|
33
|
+
params["orgPublicSpaces"] = org_public_spaces
|
|
34
|
+
if from_param is not None:
|
|
35
|
+
params["from"] = from_param
|
|
36
|
+
if to is not None:
|
|
37
|
+
params["to"] = to
|
|
38
|
+
if sort_by is not None:
|
|
39
|
+
params["sortBy"] = sort_by
|
|
40
|
+
if limit > 0:
|
|
41
|
+
params["max"] = limit
|
|
42
|
+
if offset > 0:
|
|
43
|
+
params["start"] = offset
|
|
44
|
+
try:
|
|
45
|
+
if limit > 0:
|
|
46
|
+
result = api.session.rest_get(url, params=params)
|
|
47
|
+
result = result or {}
|
|
48
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
49
|
+
else:
|
|
50
|
+
items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
|
|
51
|
+
except WebexError as e:
|
|
52
|
+
handle_rest_error(e)
|
|
53
|
+
if output == "json":
|
|
54
|
+
print_json(items)
|
|
55
|
+
else:
|
|
56
|
+
print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Type', 'type')], limit=limit)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@app.command("create")
|
|
61
|
+
def create(
|
|
62
|
+
title: str = typer.Option(None, "--title", help="(required) A user-friendly name for the room."),
|
|
63
|
+
team_id: str = typer.Option(None, "--team-id", help="The ID for the team with which this room is associated."),
|
|
64
|
+
classification_id: str = typer.Option(None, "--classification-id", help="The `classificationId` for the room."),
|
|
65
|
+
is_locked: bool = typer.Option(None, "--is-locked/--no-is-locked", help="Set the space as locked/moderated and the creator becomes a"),
|
|
66
|
+
is_public: bool = typer.Option(None, "--is-public/--no-is-public", help="The room is public and therefore discoverable within the org"),
|
|
67
|
+
description: str = typer.Option(None, "--description", help="The description of the space."),
|
|
68
|
+
is_announcement_only: bool = typer.Option(None, "--is-announcement-only/--no-is-announcement-only", help="Sets the space into announcement Mode."),
|
|
69
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
70
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
71
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
72
|
+
):
|
|
73
|
+
"""Create a Room."""
|
|
74
|
+
api = get_api(debug=debug)
|
|
75
|
+
url = f"https://webexapis.com/v1/rooms"
|
|
76
|
+
if json_body:
|
|
77
|
+
body = json.loads(json_body)
|
|
78
|
+
else:
|
|
79
|
+
body = {}
|
|
80
|
+
if title is not None:
|
|
81
|
+
body["title"] = title
|
|
82
|
+
if team_id is not None:
|
|
83
|
+
body["teamId"] = team_id
|
|
84
|
+
if classification_id is not None:
|
|
85
|
+
body["classificationId"] = classification_id
|
|
86
|
+
if is_locked is not None:
|
|
87
|
+
body["isLocked"] = is_locked
|
|
88
|
+
if is_public is not None:
|
|
89
|
+
body["isPublic"] = is_public
|
|
90
|
+
if description is not None:
|
|
91
|
+
body["description"] = description
|
|
92
|
+
if is_announcement_only is not None:
|
|
93
|
+
body["isAnnouncementOnly"] = is_announcement_only
|
|
94
|
+
_missing = [f for f in ['title'] if f not in body or body[f] is None]
|
|
95
|
+
if _missing:
|
|
96
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
97
|
+
raise typer.Exit(1)
|
|
98
|
+
try:
|
|
99
|
+
result = api.session.rest_post(url, json=body)
|
|
100
|
+
except WebexError as e:
|
|
101
|
+
handle_rest_error(e)
|
|
102
|
+
if output == "json":
|
|
103
|
+
print_json(result)
|
|
104
|
+
elif isinstance(result, dict) and "id" in result:
|
|
105
|
+
typer.echo(f"Created: {result['id']}")
|
|
106
|
+
elif not result or result == {}:
|
|
107
|
+
typer.echo("Created.")
|
|
108
|
+
else:
|
|
109
|
+
print_json(result)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@app.command("show")
|
|
114
|
+
def show(
|
|
115
|
+
room_id: str = typer.Argument(help="roomId"),
|
|
116
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
117
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
118
|
+
):
|
|
119
|
+
"""Get Room Details."""
|
|
120
|
+
api = get_api(debug=debug)
|
|
121
|
+
url = f"https://webexapis.com/v1/rooms/{room_id}"
|
|
122
|
+
try:
|
|
123
|
+
result = api.session.rest_get(url)
|
|
124
|
+
except WebexError as e:
|
|
125
|
+
handle_rest_error(e)
|
|
126
|
+
if output == "json":
|
|
127
|
+
print_json(result)
|
|
128
|
+
else:
|
|
129
|
+
if isinstance(result, dict):
|
|
130
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
131
|
+
elif isinstance(result, list):
|
|
132
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
133
|
+
else:
|
|
134
|
+
print_json(result)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@app.command("update")
|
|
139
|
+
def update(
|
|
140
|
+
room_id: str = typer.Argument(help="roomId"),
|
|
141
|
+
title: str = typer.Option(None, "--title", help="A user-friendly name for the room."),
|
|
142
|
+
classification_id: str = typer.Option(None, "--classification-id", help="The classificationId for the room."),
|
|
143
|
+
team_id: str = typer.Option(None, "--team-id", help="The teamId to which this space should be assigned. Only unow"),
|
|
144
|
+
is_locked: bool = typer.Option(None, "--is-locked/--no-is-locked", help="Set the space as locked/moderated and the creator becomes a"),
|
|
145
|
+
is_public: bool = typer.Option(None, "--is-public/--no-is-public", help="The room is public and therefore discoverable within the org"),
|
|
146
|
+
description: str = typer.Option(None, "--description", help="The description of the space."),
|
|
147
|
+
is_announcement_only: bool = typer.Option(None, "--is-announcement-only/--no-is-announcement-only", help="Sets the space into Announcement Mode or clears the Anouncem"),
|
|
148
|
+
is_read_only: bool = typer.Option(None, "--is-read-only/--no-is-read-only", help="A compliance officer can set a direct room as read-only, whi"),
|
|
149
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
150
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
151
|
+
):
|
|
152
|
+
"""Update a Room."""
|
|
153
|
+
api = get_api(debug=debug)
|
|
154
|
+
url = f"https://webexapis.com/v1/rooms/{room_id}"
|
|
155
|
+
if json_body:
|
|
156
|
+
body = json.loads(json_body)
|
|
157
|
+
else:
|
|
158
|
+
body = {}
|
|
159
|
+
if title is not None:
|
|
160
|
+
body["title"] = title
|
|
161
|
+
if classification_id is not None:
|
|
162
|
+
body["classificationId"] = classification_id
|
|
163
|
+
if team_id is not None:
|
|
164
|
+
body["teamId"] = team_id
|
|
165
|
+
if is_locked is not None:
|
|
166
|
+
body["isLocked"] = is_locked
|
|
167
|
+
if is_public is not None:
|
|
168
|
+
body["isPublic"] = is_public
|
|
169
|
+
if description is not None:
|
|
170
|
+
body["description"] = description
|
|
171
|
+
if is_announcement_only is not None:
|
|
172
|
+
body["isAnnouncementOnly"] = is_announcement_only
|
|
173
|
+
if is_read_only is not None:
|
|
174
|
+
body["isReadOnly"] = is_read_only
|
|
175
|
+
try:
|
|
176
|
+
result = api.session.rest_put(url, json=body)
|
|
177
|
+
except WebexError as e:
|
|
178
|
+
handle_rest_error(e)
|
|
179
|
+
typer.echo(f"Updated.")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@app.command("delete")
|
|
184
|
+
def delete(
|
|
185
|
+
room_id: str = typer.Argument(help="roomId"),
|
|
186
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
187
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
188
|
+
):
|
|
189
|
+
"""Delete a Room."""
|
|
190
|
+
if not force:
|
|
191
|
+
typer.confirm(f"Delete {room_id}?", abort=True)
|
|
192
|
+
api = get_api(debug=debug)
|
|
193
|
+
url = f"https://webexapis.com/v1/rooms/{room_id}"
|
|
194
|
+
try:
|
|
195
|
+
api.session.rest_delete(url)
|
|
196
|
+
except WebexError as e:
|
|
197
|
+
handle_rest_error(e)
|
|
198
|
+
typer.echo(f"Deleted: {room_id}")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@app.command("show-meeting-info")
|
|
203
|
+
def show_meeting_info(
|
|
204
|
+
room_id: str = typer.Argument(help="roomId"),
|
|
205
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
206
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
207
|
+
):
|
|
208
|
+
"""Get Room Meeting Details."""
|
|
209
|
+
api = get_api(debug=debug)
|
|
210
|
+
url = f"https://webexapis.com/v1/rooms/{room_id}/meetingInfo"
|
|
211
|
+
try:
|
|
212
|
+
result = api.session.rest_get(url)
|
|
213
|
+
except WebexError as e:
|
|
214
|
+
handle_rest_error(e)
|
|
215
|
+
if output == "json":
|
|
216
|
+
print_json(result)
|
|
217
|
+
else:
|
|
218
|
+
if isinstance(result, dict):
|
|
219
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
220
|
+
elif isinstance(result, list):
|
|
221
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
222
|
+
else:
|
|
223
|
+
print_json(result)
|
|
224
|
+
|
|
225
|
+
|