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,54 @@
|
|
|
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 security-audit.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
start_time: str = typer.Option(..., "--start-time", help="List events which occurred after a specific date and time."),
|
|
15
|
+
end_time: str = typer.Option(..., "--end-time", help="List events which occurred before a specific date and time."),
|
|
16
|
+
actor_id: str = typer.Option(None, "--actor-id", help="List events performed by this person, by ID."),
|
|
17
|
+
event_categories: str = typer.Option(None, "--event-categories", help="List events, by event categories."),
|
|
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 Security Audit Events."""
|
|
24
|
+
api = get_api(debug=debug)
|
|
25
|
+
url = f"https://webexapis.com/v1/admin/securityAudit/events"
|
|
26
|
+
params = {}
|
|
27
|
+
if start_time is not None:
|
|
28
|
+
params["startTime"] = start_time
|
|
29
|
+
if end_time is not None:
|
|
30
|
+
params["endTime"] = end_time
|
|
31
|
+
if actor_id is not None:
|
|
32
|
+
params["actorId"] = actor_id
|
|
33
|
+
if event_categories is not None:
|
|
34
|
+
params["eventCategories"] = event_categories
|
|
35
|
+
if limit > 0:
|
|
36
|
+
params["max"] = limit
|
|
37
|
+
if offset > 0:
|
|
38
|
+
params["start"] = offset
|
|
39
|
+
org_id = get_org_id()
|
|
40
|
+
if org_id is not None:
|
|
41
|
+
params["orgId"] = org_id
|
|
42
|
+
result = None
|
|
43
|
+
try:
|
|
44
|
+
result = api.session.rest_get(url, params=params)
|
|
45
|
+
except WebexError as e:
|
|
46
|
+
handle_rest_error(e)
|
|
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
|
+
if output == "json":
|
|
50
|
+
print_json(items)
|
|
51
|
+
else:
|
|
52
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
53
|
+
|
|
54
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
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 service-apps.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("create")
|
|
12
|
+
def create(
|
|
13
|
+
application_id: str = typer.Argument(help="applicationId"),
|
|
14
|
+
client_id: str = typer.Option(None, "--client-id", help="(required) The OAuth client ID of the Service App"),
|
|
15
|
+
client_secret: str = typer.Option(None, "--client-secret", help="(required) The OAuth client secret of the Service App"),
|
|
16
|
+
target_org_id: str = typer.Option(None, "--target-org-id", help="(required) The organization ID for which the token will be created"),
|
|
17
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
18
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
19
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
20
|
+
):
|
|
21
|
+
"""Create Service App Access Token."""
|
|
22
|
+
api = get_api(debug=debug)
|
|
23
|
+
url = f"https://webexapis.com/v1/applications/{application_id}/token"
|
|
24
|
+
if json_body:
|
|
25
|
+
body = json.loads(json_body)
|
|
26
|
+
else:
|
|
27
|
+
body = {}
|
|
28
|
+
if client_id is not None:
|
|
29
|
+
body["clientId"] = client_id
|
|
30
|
+
if client_secret is not None:
|
|
31
|
+
body["clientSecret"] = client_secret
|
|
32
|
+
if target_org_id is not None:
|
|
33
|
+
body["targetOrgId"] = target_org_id
|
|
34
|
+
_missing = [f for f in ['clientId', 'clientSecret', 'targetOrgId'] if f not in body or body[f] is None]
|
|
35
|
+
if _missing:
|
|
36
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
37
|
+
raise typer.Exit(1)
|
|
38
|
+
try:
|
|
39
|
+
result = api.session.rest_post(url, json=body)
|
|
40
|
+
except WebexError as e:
|
|
41
|
+
handle_rest_error(e)
|
|
42
|
+
if output == "json":
|
|
43
|
+
print_json(result)
|
|
44
|
+
elif isinstance(result, dict) and "id" in result:
|
|
45
|
+
typer.echo(f"Created: {result['id']}")
|
|
46
|
+
elif not result or result == {}:
|
|
47
|
+
typer.echo("Created.")
|
|
48
|
+
else:
|
|
49
|
+
print_json(result)
|
|
50
|
+
|
|
51
|
+
|
|
@@ -0,0 +1,209 @@
|
|
|
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 single-number-reach.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
15
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
|
|
16
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
17
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
18
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
19
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
20
|
+
):
|
|
21
|
+
"""Get Single Number Reach Primary Available Phone Numbers."""
|
|
22
|
+
api = get_api(debug=debug)
|
|
23
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/singleNumberReach/availableNumbers"
|
|
24
|
+
params = {}
|
|
25
|
+
if phone_number is not None:
|
|
26
|
+
params["phoneNumber"] = phone_number
|
|
27
|
+
if limit > 0:
|
|
28
|
+
params["max"] = limit
|
|
29
|
+
if offset > 0:
|
|
30
|
+
params["start"] = offset
|
|
31
|
+
org_id = get_org_id()
|
|
32
|
+
if org_id is not None:
|
|
33
|
+
params["orgId"] = org_id
|
|
34
|
+
result = None
|
|
35
|
+
try:
|
|
36
|
+
result = api.session.rest_get(url, params=params)
|
|
37
|
+
except WebexError as e:
|
|
38
|
+
handle_rest_error(e)
|
|
39
|
+
result = result or []
|
|
40
|
+
items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
41
|
+
if output == "json":
|
|
42
|
+
print_json(items)
|
|
43
|
+
else:
|
|
44
|
+
print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@app.command("create")
|
|
49
|
+
def create(
|
|
50
|
+
person_id: str = typer.Argument(help="personId"),
|
|
51
|
+
phone_number: str = typer.Option(None, "--phone-number", help="(required) Personal phone number used as single Number Reach."),
|
|
52
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="(required) A flag to enable or disable single Number Reach."),
|
|
53
|
+
name: str = typer.Option(None, "--name", help="(required) Name of the single number reach phone number entry."),
|
|
54
|
+
do_not_forward_calls_enabled: bool = typer.Option(None, "--do-not-forward-calls-enabled/--no-do-not-forward-calls-enabled", help="If enabled, the call forwarding settings of provided phone N"),
|
|
55
|
+
answer_confirmation_enabled: bool = typer.Option(None, "--answer-confirmation-enabled/--no-answer-confirmation-enabled", help="If enabled, the call recepient will be prompted to press a k"),
|
|
56
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
57
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
58
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
59
|
+
):
|
|
60
|
+
"""Create Single Number Reach For a Person."""
|
|
61
|
+
api = get_api(debug=debug)
|
|
62
|
+
url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach/numbers"
|
|
63
|
+
if json_body:
|
|
64
|
+
body = json.loads(json_body)
|
|
65
|
+
else:
|
|
66
|
+
body = {}
|
|
67
|
+
if phone_number is not None:
|
|
68
|
+
body["phoneNumber"] = phone_number
|
|
69
|
+
if enabled is not None:
|
|
70
|
+
body["enabled"] = enabled
|
|
71
|
+
if name is not None:
|
|
72
|
+
body["name"] = name
|
|
73
|
+
if do_not_forward_calls_enabled is not None:
|
|
74
|
+
body["doNotForwardCallsEnabled"] = do_not_forward_calls_enabled
|
|
75
|
+
if answer_confirmation_enabled is not None:
|
|
76
|
+
body["answerConfirmationEnabled"] = answer_confirmation_enabled
|
|
77
|
+
_missing = [f for f in ['phoneNumber', 'enabled', 'name'] if f not in body or body[f] is None]
|
|
78
|
+
if _missing:
|
|
79
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
80
|
+
raise typer.Exit(1)
|
|
81
|
+
try:
|
|
82
|
+
result = api.session.rest_post(url, json=body)
|
|
83
|
+
except WebexError as e:
|
|
84
|
+
handle_rest_error(e)
|
|
85
|
+
if output == "json":
|
|
86
|
+
print_json(result)
|
|
87
|
+
elif isinstance(result, dict) and "id" in result:
|
|
88
|
+
typer.echo(f"Created: {result['id']}")
|
|
89
|
+
elif not result or result == {}:
|
|
90
|
+
typer.echo("Created.")
|
|
91
|
+
else:
|
|
92
|
+
print_json(result)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@app.command("list-single-number-reach")
|
|
97
|
+
def list_single_number_reach(
|
|
98
|
+
person_id: str = typer.Argument(help="personId"),
|
|
99
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
100
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
101
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
102
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
103
|
+
):
|
|
104
|
+
"""Get Single Number Reach Settings For A Person."""
|
|
105
|
+
api = get_api(debug=debug)
|
|
106
|
+
url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach"
|
|
107
|
+
params = {}
|
|
108
|
+
if limit > 0:
|
|
109
|
+
params["max"] = limit
|
|
110
|
+
if offset > 0:
|
|
111
|
+
params["start"] = offset
|
|
112
|
+
result = None
|
|
113
|
+
try:
|
|
114
|
+
result = api.session.rest_get(url, params=params)
|
|
115
|
+
except WebexError as e:
|
|
116
|
+
handle_rest_error(e)
|
|
117
|
+
result = result or []
|
|
118
|
+
items = result.get("numbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
119
|
+
if output == "json":
|
|
120
|
+
print_json(items)
|
|
121
|
+
else:
|
|
122
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.command("update")
|
|
127
|
+
def update(
|
|
128
|
+
person_id: str = typer.Argument(help="personId"),
|
|
129
|
+
alert_all_numbers_for_click_to_dial_calls_enabled: bool = typer.Option(None, "--alert-all-numbers-for-click-to-dial-calls-enabled/--no-alert-all-numbers-for-click-to-dial-calls-enabled", help="Flag to enable alerting single number reach numbers for clic"),
|
|
130
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
131
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
132
|
+
):
|
|
133
|
+
"""Update Single Number Reach Settings For A Person."""
|
|
134
|
+
api = get_api(debug=debug)
|
|
135
|
+
url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach"
|
|
136
|
+
if json_body:
|
|
137
|
+
body = json.loads(json_body)
|
|
138
|
+
else:
|
|
139
|
+
body = {}
|
|
140
|
+
if alert_all_numbers_for_click_to_dial_calls_enabled is not None:
|
|
141
|
+
body["alertAllNumbersForClickToDialCallsEnabled"] = alert_all_numbers_for_click_to_dial_calls_enabled
|
|
142
|
+
try:
|
|
143
|
+
result = api.session.rest_put(url, json=body)
|
|
144
|
+
except WebexError as e:
|
|
145
|
+
handle_rest_error(e)
|
|
146
|
+
typer.echo(f"Updated.")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@app.command("update-numbers")
|
|
151
|
+
def update_numbers(
|
|
152
|
+
person_id: str = typer.Argument(help="personId"),
|
|
153
|
+
id: str = typer.Argument(help="id"),
|
|
154
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Personal phone number used as single Number Reach."),
|
|
155
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="A flag to enable or disable single Number Reach phone number"),
|
|
156
|
+
name: str = typer.Option(None, "--name", help="Name of the single number reach phone number entry."),
|
|
157
|
+
do_not_forward_calls_enabled: bool = typer.Option(None, "--do-not-forward-calls-enabled/--no-do-not-forward-calls-enabled", help="If enabled, the call forwarding settings of provided phone N"),
|
|
158
|
+
answer_confirmation_enabled: bool = typer.Option(None, "--answer-confirmation-enabled/--no-answer-confirmation-enabled", help="If enabled, the call recepient will be prompted to press a k"),
|
|
159
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
160
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
161
|
+
):
|
|
162
|
+
"""Update Single Number Reach Settings For A Number."""
|
|
163
|
+
api = get_api(debug=debug)
|
|
164
|
+
url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach/numbers/{id}"
|
|
165
|
+
if json_body:
|
|
166
|
+
body = json.loads(json_body)
|
|
167
|
+
else:
|
|
168
|
+
body = {}
|
|
169
|
+
if phone_number is not None:
|
|
170
|
+
body["phoneNumber"] = phone_number
|
|
171
|
+
if enabled is not None:
|
|
172
|
+
body["enabled"] = enabled
|
|
173
|
+
if name is not None:
|
|
174
|
+
body["name"] = name
|
|
175
|
+
if do_not_forward_calls_enabled is not None:
|
|
176
|
+
body["doNotForwardCallsEnabled"] = do_not_forward_calls_enabled
|
|
177
|
+
if answer_confirmation_enabled is not None:
|
|
178
|
+
body["answerConfirmationEnabled"] = answer_confirmation_enabled
|
|
179
|
+
try:
|
|
180
|
+
result = api.session.rest_put(url, json=body)
|
|
181
|
+
except WebexError as e:
|
|
182
|
+
handle_rest_error(e)
|
|
183
|
+
typer.echo(f"Updated.")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@app.command("delete")
|
|
188
|
+
def delete(
|
|
189
|
+
person_id: str = typer.Argument(help="personId"),
|
|
190
|
+
id: str = typer.Argument(help="id"),
|
|
191
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
192
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
193
|
+
):
|
|
194
|
+
"""Delete A Single Number Reach Number."""
|
|
195
|
+
if not force:
|
|
196
|
+
typer.confirm(f"Delete {id}?", abort=True)
|
|
197
|
+
api = get_api(debug=debug)
|
|
198
|
+
url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach/numbers/{id}"
|
|
199
|
+
params = {}
|
|
200
|
+
org_id = get_org_id()
|
|
201
|
+
if org_id is not None:
|
|
202
|
+
params["orgId"] = org_id
|
|
203
|
+
try:
|
|
204
|
+
api.session.rest_delete(url, params=params)
|
|
205
|
+
except WebexError as e:
|
|
206
|
+
handle_rest_error(e)
|
|
207
|
+
typer.echo(f"Deleted: {id}")
|
|
208
|
+
|
|
209
|
+
|
|
@@ -0,0 +1,155 @@
|
|
|
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 team-memberships.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("list")
|
|
12
|
+
def cmd_list(
|
|
13
|
+
team_id: str = typer.Option(..., "--team-id", help="List memberships for a team, by ID."),
|
|
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 Team Memberships."""
|
|
20
|
+
api = get_api(debug=debug)
|
|
21
|
+
url = f"https://webexapis.com/v1/team/memberships"
|
|
22
|
+
params = {}
|
|
23
|
+
if team_id is not None:
|
|
24
|
+
params["teamId"] = team_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
|
+
items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
|
|
36
|
+
except WebexError as e:
|
|
37
|
+
handle_rest_error(e)
|
|
38
|
+
if output == "json":
|
|
39
|
+
print_json(items)
|
|
40
|
+
else:
|
|
41
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@app.command("create")
|
|
46
|
+
def create(
|
|
47
|
+
team_id: str = typer.Option(None, "--team-id", help="(required) The team ID."),
|
|
48
|
+
person_id: str = typer.Option(None, "--person-id", help="The person ID."),
|
|
49
|
+
person_email: str = typer.Option(None, "--person-email", help="The email address of the person."),
|
|
50
|
+
is_moderator: bool = typer.Option(None, "--is-moderator/--no-is-moderator", help="Whether or not the participant is a team moderator."),
|
|
51
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
52
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
53
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
54
|
+
):
|
|
55
|
+
"""Create a Team Membership."""
|
|
56
|
+
api = get_api(debug=debug)
|
|
57
|
+
url = f"https://webexapis.com/v1/team/memberships"
|
|
58
|
+
if json_body:
|
|
59
|
+
body = json.loads(json_body)
|
|
60
|
+
else:
|
|
61
|
+
body = {}
|
|
62
|
+
if team_id is not None:
|
|
63
|
+
body["teamId"] = team_id
|
|
64
|
+
if person_id is not None:
|
|
65
|
+
body["personId"] = person_id
|
|
66
|
+
if person_email is not None:
|
|
67
|
+
body["personEmail"] = person_email
|
|
68
|
+
if is_moderator is not None:
|
|
69
|
+
body["isModerator"] = is_moderator
|
|
70
|
+
_missing = [f for f in ['teamId'] if f not in body or body[f] is None]
|
|
71
|
+
if _missing:
|
|
72
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
73
|
+
raise typer.Exit(1)
|
|
74
|
+
try:
|
|
75
|
+
result = api.session.rest_post(url, json=body)
|
|
76
|
+
except WebexError as e:
|
|
77
|
+
handle_rest_error(e)
|
|
78
|
+
if output == "json":
|
|
79
|
+
print_json(result)
|
|
80
|
+
elif isinstance(result, dict) and "id" in result:
|
|
81
|
+
typer.echo(f"Created: {result['id']}")
|
|
82
|
+
elif not result or result == {}:
|
|
83
|
+
typer.echo("Created.")
|
|
84
|
+
else:
|
|
85
|
+
print_json(result)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.command("show")
|
|
90
|
+
def show(
|
|
91
|
+
membership_id: str = typer.Argument(help="membershipId"),
|
|
92
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
93
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
94
|
+
):
|
|
95
|
+
"""Get Team Membership Details."""
|
|
96
|
+
api = get_api(debug=debug)
|
|
97
|
+
url = f"https://webexapis.com/v1/team/memberships/{membership_id}"
|
|
98
|
+
try:
|
|
99
|
+
result = api.session.rest_get(url)
|
|
100
|
+
except WebexError as e:
|
|
101
|
+
handle_rest_error(e)
|
|
102
|
+
if output == "json":
|
|
103
|
+
print_json(result)
|
|
104
|
+
else:
|
|
105
|
+
if isinstance(result, dict):
|
|
106
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
107
|
+
elif isinstance(result, list):
|
|
108
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
109
|
+
else:
|
|
110
|
+
print_json(result)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@app.command("update")
|
|
115
|
+
def update(
|
|
116
|
+
membership_id: str = typer.Argument(help="membershipId"),
|
|
117
|
+
is_moderator: bool = typer.Option(None, "--is-moderator/--no-is-moderator", help="Whether or not the participant is a team moderator."),
|
|
118
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
119
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
120
|
+
):
|
|
121
|
+
"""Update a Team Membership."""
|
|
122
|
+
api = get_api(debug=debug)
|
|
123
|
+
url = f"https://webexapis.com/v1/team/memberships/{membership_id}"
|
|
124
|
+
if json_body:
|
|
125
|
+
body = json.loads(json_body)
|
|
126
|
+
else:
|
|
127
|
+
body = {}
|
|
128
|
+
if is_moderator is not None:
|
|
129
|
+
body["isModerator"] = is_moderator
|
|
130
|
+
try:
|
|
131
|
+
result = api.session.rest_put(url, json=body)
|
|
132
|
+
except WebexError as e:
|
|
133
|
+
handle_rest_error(e)
|
|
134
|
+
typer.echo(f"Updated.")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@app.command("delete")
|
|
139
|
+
def delete(
|
|
140
|
+
membership_id: str = typer.Argument(help="membershipId"),
|
|
141
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
142
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
143
|
+
):
|
|
144
|
+
"""Delete a Team Membership."""
|
|
145
|
+
if not force:
|
|
146
|
+
typer.confirm(f"Delete {membership_id}?", abort=True)
|
|
147
|
+
api = get_api(debug=debug)
|
|
148
|
+
url = f"https://webexapis.com/v1/team/memberships/{membership_id}"
|
|
149
|
+
try:
|
|
150
|
+
api.session.rest_delete(url)
|
|
151
|
+
except WebexError as e:
|
|
152
|
+
handle_rest_error(e)
|
|
153
|
+
typer.echo(f"Deleted: {membership_id}")
|
|
154
|
+
|
|
155
|
+
|
wxcli/commands/teams.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
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 teams.")
|
|
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 Teams."""
|
|
19
|
+
api = get_api(debug=debug)
|
|
20
|
+
url = f"https://webexapis.com/v1/teams"
|
|
21
|
+
params = {}
|
|
22
|
+
if limit > 0:
|
|
23
|
+
params["max"] = limit
|
|
24
|
+
if offset > 0:
|
|
25
|
+
params["start"] = offset
|
|
26
|
+
try:
|
|
27
|
+
if limit > 0:
|
|
28
|
+
result = api.session.rest_get(url, params=params)
|
|
29
|
+
result = result or {}
|
|
30
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
31
|
+
else:
|
|
32
|
+
items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
|
|
33
|
+
except WebexError as e:
|
|
34
|
+
handle_rest_error(e)
|
|
35
|
+
if output == "json":
|
|
36
|
+
print_json(items)
|
|
37
|
+
else:
|
|
38
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name')], limit=limit)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@app.command("create")
|
|
43
|
+
def create(
|
|
44
|
+
name: str = typer.Option(None, "--name", help="(required) A user-friendly name for the team."),
|
|
45
|
+
description: str = typer.Option(None, "--description", help="The teams description."),
|
|
46
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
47
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
48
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
49
|
+
):
|
|
50
|
+
"""Create a Team."""
|
|
51
|
+
api = get_api(debug=debug)
|
|
52
|
+
url = f"https://webexapis.com/v1/teams"
|
|
53
|
+
if json_body:
|
|
54
|
+
body = json.loads(json_body)
|
|
55
|
+
else:
|
|
56
|
+
body = {}
|
|
57
|
+
if name is not None:
|
|
58
|
+
body["name"] = name
|
|
59
|
+
if description is not None:
|
|
60
|
+
body["description"] = description
|
|
61
|
+
_missing = [f for f in ['name'] if f not in body or body[f] is None]
|
|
62
|
+
if _missing:
|
|
63
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
64
|
+
raise typer.Exit(1)
|
|
65
|
+
try:
|
|
66
|
+
result = api.session.rest_post(url, json=body)
|
|
67
|
+
except WebexError as e:
|
|
68
|
+
handle_rest_error(e)
|
|
69
|
+
if output == "json":
|
|
70
|
+
print_json(result)
|
|
71
|
+
elif isinstance(result, dict) and "id" in result:
|
|
72
|
+
typer.echo(f"Created: {result['id']}")
|
|
73
|
+
elif not result or result == {}:
|
|
74
|
+
typer.echo("Created.")
|
|
75
|
+
else:
|
|
76
|
+
print_json(result)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@app.command("show")
|
|
81
|
+
def show(
|
|
82
|
+
team_id: str = typer.Argument(help="teamId"),
|
|
83
|
+
description: str = typer.Option(None, "--description", help="The teams description."),
|
|
84
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
85
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
86
|
+
):
|
|
87
|
+
"""Get Team Details."""
|
|
88
|
+
api = get_api(debug=debug)
|
|
89
|
+
url = f"https://webexapis.com/v1/teams/{team_id}"
|
|
90
|
+
params = {}
|
|
91
|
+
if description is not None:
|
|
92
|
+
params["description"] = description
|
|
93
|
+
try:
|
|
94
|
+
result = api.session.rest_get(url, params=params)
|
|
95
|
+
except WebexError as e:
|
|
96
|
+
handle_rest_error(e)
|
|
97
|
+
if output == "json":
|
|
98
|
+
print_json(result)
|
|
99
|
+
else:
|
|
100
|
+
if isinstance(result, dict):
|
|
101
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
102
|
+
elif isinstance(result, list):
|
|
103
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
104
|
+
else:
|
|
105
|
+
print_json(result)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@app.command("update")
|
|
110
|
+
def update(
|
|
111
|
+
team_id: str = typer.Argument(help="teamId"),
|
|
112
|
+
name: str = typer.Option(None, "--name", help="A user-friendly name for the team."),
|
|
113
|
+
description: str = typer.Option(None, "--description", help="The teams description."),
|
|
114
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
115
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
116
|
+
):
|
|
117
|
+
"""Update a Team."""
|
|
118
|
+
api = get_api(debug=debug)
|
|
119
|
+
url = f"https://webexapis.com/v1/teams/{team_id}"
|
|
120
|
+
if json_body:
|
|
121
|
+
body = json.loads(json_body)
|
|
122
|
+
else:
|
|
123
|
+
body = {}
|
|
124
|
+
if name is not None:
|
|
125
|
+
body["name"] = name
|
|
126
|
+
if description is not None:
|
|
127
|
+
body["description"] = description
|
|
128
|
+
try:
|
|
129
|
+
result = api.session.rest_put(url, json=body)
|
|
130
|
+
except WebexError as e:
|
|
131
|
+
handle_rest_error(e)
|
|
132
|
+
typer.echo(f"Updated.")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@app.command("delete")
|
|
137
|
+
def delete(
|
|
138
|
+
team_id: str = typer.Argument(help="teamId"),
|
|
139
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
140
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
141
|
+
):
|
|
142
|
+
"""Delete a Team."""
|
|
143
|
+
if not force:
|
|
144
|
+
typer.confirm(f"Delete {team_id}?", abort=True)
|
|
145
|
+
api = get_api(debug=debug)
|
|
146
|
+
url = f"https://webexapis.com/v1/teams/{team_id}"
|
|
147
|
+
try:
|
|
148
|
+
api.session.rest_delete(url)
|
|
149
|
+
except WebexError as e:
|
|
150
|
+
handle_rest_error(e)
|
|
151
|
+
typer.echo(f"Deleted: {team_id}")
|
|
152
|
+
|
|
153
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
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 ucm-profile.")
|
|
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
|
+
"""Read the List of UC Manager Profiles."""
|
|
20
|
+
api = get_api(debug=debug)
|
|
21
|
+
url = f"https://webexapis.com/v1/telephony/config/callingProfiles"
|
|
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("callingProfiles", 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
|
+
|