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,523 @@
|
|
|
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 wholesale-provisioning.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
external_id: str = typer.Option(None, "--external-id", help="Customer external ID."),
|
|
15
|
+
status: str = typer.Option(None, "--status", help="Customer API status."),
|
|
16
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
17
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
18
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
19
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
20
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
21
|
+
):
|
|
22
|
+
"""List Wholesale Customers."""
|
|
23
|
+
api = get_api(debug=debug)
|
|
24
|
+
url = f"https://webexapis.com/v1/wholesale/customers"
|
|
25
|
+
params = {}
|
|
26
|
+
if external_id is not None:
|
|
27
|
+
params["externalId"] = external_id
|
|
28
|
+
if status is not None:
|
|
29
|
+
params["status"] = status
|
|
30
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
31
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
32
|
+
if limit > 0:
|
|
33
|
+
params["max"] = limit
|
|
34
|
+
if offset > 0:
|
|
35
|
+
params["offset"] = offset
|
|
36
|
+
org_id = get_org_id()
|
|
37
|
+
if org_id is not None:
|
|
38
|
+
params["orgId"] = org_id
|
|
39
|
+
result = None
|
|
40
|
+
try:
|
|
41
|
+
result = api.session.rest_get(url, params=params)
|
|
42
|
+
except WebexError as e:
|
|
43
|
+
handle_rest_error(e)
|
|
44
|
+
result = result or []
|
|
45
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
46
|
+
if output == "json":
|
|
47
|
+
print_json(items)
|
|
48
|
+
else:
|
|
49
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@app.command("create")
|
|
54
|
+
def create(
|
|
55
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
56
|
+
provisioning_id: str = typer.Option(None, "--provisioning-id", help="(required) This Provisioning ID defines how this customer is to be prov"),
|
|
57
|
+
org_id: str = typer.Option(None, "--org-id", help="The organization ID of the enterprise in Webex. Mandatory fo"),
|
|
58
|
+
external_id: str = typer.Option(None, "--external-id", help="(required) External ID of the Wholesale customer."),
|
|
59
|
+
sub_partner_admin_email: str = typer.Option(None, "--sub-partner-admin-email", help="The email of the sub partner organization admin."),
|
|
60
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
61
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
62
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
63
|
+
):
|
|
64
|
+
"""Provision a Wholesale Customer\n\nExample --json-body:\n '{"provisioningId":"...","packages":["common_area_calling"],"externalId":"...","address":{"addressLine1":"...","city":"...","country":"...","addressLine2":"...","stateOrProvince":"...","zipOrPostalCode":"..."},"orgId":"...","customerInfo":{"name":"...","primaryEmail":"...","language":"..."},"provisioningParameters":{"calling":{"location":"..."},"meetings":{"timezone":"..."},"packages":{"limits":"..."}},"subPartnerAdminEmail":"..."}'."""
|
|
65
|
+
api = get_api(debug=debug)
|
|
66
|
+
url = f"https://webexapis.com/v1/wholesale/customers"
|
|
67
|
+
params = {}
|
|
68
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
69
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
70
|
+
if json_body:
|
|
71
|
+
body = json.loads(json_body)
|
|
72
|
+
else:
|
|
73
|
+
body = {}
|
|
74
|
+
if provisioning_id is not None:
|
|
75
|
+
body["provisioningId"] = provisioning_id
|
|
76
|
+
if org_id is not None:
|
|
77
|
+
body["orgId"] = org_id
|
|
78
|
+
if external_id is not None:
|
|
79
|
+
body["externalId"] = external_id
|
|
80
|
+
if sub_partner_admin_email is not None:
|
|
81
|
+
body["subPartnerAdminEmail"] = sub_partner_admin_email
|
|
82
|
+
_missing = [f for f in ['provisioningId', 'externalId'] if f not in body or body[f] is None]
|
|
83
|
+
if _missing:
|
|
84
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
85
|
+
raise typer.Exit(1)
|
|
86
|
+
try:
|
|
87
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
88
|
+
except WebexError as e:
|
|
89
|
+
handle_rest_error(e)
|
|
90
|
+
if output == "json":
|
|
91
|
+
print_json(result)
|
|
92
|
+
elif isinstance(result, dict) and "id" in result:
|
|
93
|
+
typer.echo(f"Created: {result['id']}")
|
|
94
|
+
elif not result or result == {}:
|
|
95
|
+
typer.echo("Created.")
|
|
96
|
+
else:
|
|
97
|
+
print_json(result)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@app.command("show")
|
|
102
|
+
def show(
|
|
103
|
+
customer_id: str = typer.Argument(help="customerId"),
|
|
104
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
105
|
+
include_package_license_info: str = typer.Option(None, "--include-package-license-info", help="If specified as true, a list of licenseIds will be returned"),
|
|
106
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
107
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
108
|
+
):
|
|
109
|
+
"""Get a Wholesale Customer."""
|
|
110
|
+
api = get_api(debug=debug)
|
|
111
|
+
url = f"https://webexapis.com/v1/wholesale/customers/{customer_id}"
|
|
112
|
+
params = {}
|
|
113
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
114
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
115
|
+
if include_package_license_info is not None:
|
|
116
|
+
params["includePackageLicenseInfo"] = include_package_license_info
|
|
117
|
+
try:
|
|
118
|
+
result = api.session.rest_get(url, params=params)
|
|
119
|
+
except WebexError as e:
|
|
120
|
+
handle_rest_error(e)
|
|
121
|
+
if output == "json":
|
|
122
|
+
print_json(result)
|
|
123
|
+
else:
|
|
124
|
+
if isinstance(result, dict):
|
|
125
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
126
|
+
elif isinstance(result, list):
|
|
127
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
128
|
+
else:
|
|
129
|
+
print_json(result)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@app.command("update")
|
|
134
|
+
def update(
|
|
135
|
+
customer_id: str = typer.Argument(help="customerId"),
|
|
136
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
137
|
+
external_id: str = typer.Option(None, "--external-id", help="External ID of the Wholesale customer."),
|
|
138
|
+
sub_partner_admin_email: str = typer.Option(None, "--sub-partner-admin-email", help="The email of the sub partner organization admin."),
|
|
139
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
140
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
141
|
+
):
|
|
142
|
+
"""Update a Wholesale Customer\n\nExample --json-body:\n '{"packages":["common_area_calling"],"externalId":"...","address":{"addressLine1":"...","city":"...","country":"...","addressLine2":"...","stateOrProvince":"...","zipOrPostalCode":"..."},"provisioningParameters":{"calling":{"location":"..."},"meetings":{"timezone":"..."},"packages":{"limits":"..."}},"subPartnerAdminEmail":"..."}'."""
|
|
143
|
+
api = get_api(debug=debug)
|
|
144
|
+
url = f"https://webexapis.com/v1/wholesale/customers/{customer_id}"
|
|
145
|
+
params = {}
|
|
146
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
147
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
148
|
+
if json_body:
|
|
149
|
+
body = json.loads(json_body)
|
|
150
|
+
else:
|
|
151
|
+
body = {}
|
|
152
|
+
if external_id is not None:
|
|
153
|
+
body["externalId"] = external_id
|
|
154
|
+
if sub_partner_admin_email is not None:
|
|
155
|
+
body["subPartnerAdminEmail"] = sub_partner_admin_email
|
|
156
|
+
try:
|
|
157
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
158
|
+
except WebexError as e:
|
|
159
|
+
handle_rest_error(e)
|
|
160
|
+
typer.echo(f"Updated.")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@app.command("delete")
|
|
165
|
+
def delete(
|
|
166
|
+
customer_id: str = typer.Argument(help="customerId"),
|
|
167
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
168
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
169
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
170
|
+
):
|
|
171
|
+
"""Remove a Wholesale Customer."""
|
|
172
|
+
if not force:
|
|
173
|
+
typer.confirm(f"Delete {customer_id}?", abort=True)
|
|
174
|
+
api = get_api(debug=debug)
|
|
175
|
+
url = f"https://webexapis.com/v1/wholesale/customers/{customer_id}"
|
|
176
|
+
params = {}
|
|
177
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
178
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
179
|
+
try:
|
|
180
|
+
api.session.rest_delete(url, params=params)
|
|
181
|
+
except WebexError as e:
|
|
182
|
+
handle_rest_error(e)
|
|
183
|
+
typer.echo(f"Deleted: {customer_id}")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@app.command("create-validate-customers")
|
|
188
|
+
def create_validate_customers(
|
|
189
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
190
|
+
provisioning_id: str = typer.Option(None, "--provisioning-id", help="Defines how this wholesale customer is to be provisioned for"),
|
|
191
|
+
org_id: str = typer.Option(None, "--org-id", help="The organization ID of the enterprise in Cisco Webex."),
|
|
192
|
+
external_id: str = typer.Option(None, "--external-id", help="External ID of the Wholesale customer."),
|
|
193
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
194
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
195
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
196
|
+
):
|
|
197
|
+
"""Precheck a Wholesale Customer Provisioning\n\nExample --json-body:\n '{"address":{"addressLine1":"...","city":"...","country":"...","addressLine2":"...","stateOrProvince":"...","zipOrPostalCode":"..."},"provisioningId":"...","packages":["common_area_calling"],"orgId":"...","externalId":"...","customerInfo":{"primaryEmail":"...","name":"..."},"provisioningParameters":{"calling":{"location":"..."},"meetings":{"timezone":"..."},"packages":{"limits":"..."}}}'."""
|
|
198
|
+
api = get_api(debug=debug)
|
|
199
|
+
url = f"https://webexapis.com/v1/wholesale/customers/validate"
|
|
200
|
+
params = {}
|
|
201
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
202
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
203
|
+
if json_body:
|
|
204
|
+
body = json.loads(json_body)
|
|
205
|
+
else:
|
|
206
|
+
body = {}
|
|
207
|
+
if provisioning_id is not None:
|
|
208
|
+
body["provisioningId"] = provisioning_id
|
|
209
|
+
if org_id is not None:
|
|
210
|
+
body["orgId"] = org_id
|
|
211
|
+
if external_id is not None:
|
|
212
|
+
body["externalId"] = external_id
|
|
213
|
+
try:
|
|
214
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
215
|
+
except WebexError as e:
|
|
216
|
+
handle_rest_error(e)
|
|
217
|
+
if output == "json":
|
|
218
|
+
print_json(result)
|
|
219
|
+
elif isinstance(result, dict) and "id" in result:
|
|
220
|
+
typer.echo(f"Created: {result['id']}")
|
|
221
|
+
elif not result or result == {}:
|
|
222
|
+
typer.echo("Created.")
|
|
223
|
+
else:
|
|
224
|
+
print_json(result)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@app.command("list-sub-partners")
|
|
229
|
+
def list_sub_partners(
|
|
230
|
+
provisioning_state: str = typer.Option(None, "--provisioning-state", help="Status to filter sub-partners based on provisioning state."),
|
|
231
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
232
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
233
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
234
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
235
|
+
):
|
|
236
|
+
"""List Wholesale Sub-partners."""
|
|
237
|
+
api = get_api(debug=debug)
|
|
238
|
+
url = f"https://webexapis.com/v1/wholesale/subPartners"
|
|
239
|
+
params = {}
|
|
240
|
+
if provisioning_state is not None:
|
|
241
|
+
params["provisioningState"] = provisioning_state
|
|
242
|
+
if limit > 0:
|
|
243
|
+
params["max"] = limit
|
|
244
|
+
if offset > 0:
|
|
245
|
+
params["offset"] = offset
|
|
246
|
+
result = None
|
|
247
|
+
try:
|
|
248
|
+
result = api.session.rest_get(url, params=params)
|
|
249
|
+
except WebexError as e:
|
|
250
|
+
handle_rest_error(e)
|
|
251
|
+
result = result or []
|
|
252
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
253
|
+
if output == "json":
|
|
254
|
+
print_json(items)
|
|
255
|
+
else:
|
|
256
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@app.command("list-subscribers")
|
|
261
|
+
def list_subscribers(
|
|
262
|
+
customer_id: str = typer.Option(None, "--customer-id", help="Wholesale customer ID."),
|
|
263
|
+
person_id: str = typer.Option(None, "--person-id", help="The person ID of the subscriber used in the [/v1/people API]"),
|
|
264
|
+
external_customer_id: str = typer.Option(None, "--external-customer-id", help="Customer external ID."),
|
|
265
|
+
email: str = typer.Option(None, "--email", help="The email address of the subscriber."),
|
|
266
|
+
status: str = typer.Option(None, "--status", help="The provisioning status of the subscriber."),
|
|
267
|
+
after: str = typer.Option(None, "--after", help="Only include subscribers created after this date and time. E"),
|
|
268
|
+
last_status_change: str = typer.Option(None, "--last-status-change", help="Only include subscribers with a provisioning status change a"),
|
|
269
|
+
sort_by: str = typer.Option(None, "--sort-by", help="Supported `sortBy` attributes are `created` and `lastStatusC"),
|
|
270
|
+
sort_order: str = typer.Option(None, "--sort-order", help="Sort by `ASC` (ascending) or `DESC` (descending)."),
|
|
271
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
272
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
273
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
274
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
275
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
276
|
+
):
|
|
277
|
+
"""List Wholesale Subscribers."""
|
|
278
|
+
api = get_api(debug=debug)
|
|
279
|
+
url = f"https://webexapis.com/v1/wholesale/subscribers"
|
|
280
|
+
params = {}
|
|
281
|
+
if customer_id is not None:
|
|
282
|
+
params["customerId"] = customer_id
|
|
283
|
+
if person_id is not None:
|
|
284
|
+
params["personId"] = person_id
|
|
285
|
+
if external_customer_id is not None:
|
|
286
|
+
params["externalCustomerId"] = external_customer_id
|
|
287
|
+
if email is not None:
|
|
288
|
+
params["email"] = email
|
|
289
|
+
if status is not None:
|
|
290
|
+
params["status"] = status
|
|
291
|
+
if after is not None:
|
|
292
|
+
params["after"] = after
|
|
293
|
+
if last_status_change is not None:
|
|
294
|
+
params["lastStatusChange"] = last_status_change
|
|
295
|
+
if sort_by is not None:
|
|
296
|
+
params["sortBy"] = sort_by
|
|
297
|
+
if sort_order is not None:
|
|
298
|
+
params["sortOrder"] = sort_order
|
|
299
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
300
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
301
|
+
if limit > 0:
|
|
302
|
+
params["max"] = limit
|
|
303
|
+
if offset > 0:
|
|
304
|
+
params["offset"] = offset
|
|
305
|
+
result = None
|
|
306
|
+
try:
|
|
307
|
+
result = api.session.rest_get(url, params=params)
|
|
308
|
+
except WebexError as e:
|
|
309
|
+
handle_rest_error(e)
|
|
310
|
+
result = result or []
|
|
311
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
312
|
+
if output == "json":
|
|
313
|
+
print_json(items)
|
|
314
|
+
else:
|
|
315
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@app.command("create-subscribers")
|
|
320
|
+
def create_subscribers(
|
|
321
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
322
|
+
customer_id: str = typer.Option(None, "--customer-id", help="(required) ID of the Provisioned Customer for Webex Wholesale."),
|
|
323
|
+
email: str = typer.Option(None, "--email", help="(required) The email address of the subscriber (mandatory for the trust"),
|
|
324
|
+
package: str = typer.Option(None, "--package", help="Choices: webex_calling, webex_meetings, webex_suite, webex_voice, cx_essentials, webex_calling_standard"),
|
|
325
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
326
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
327
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
328
|
+
):
|
|
329
|
+
"""Provision a Wholesale Subscriber\n\nExample --json-body:\n '{"customerId":"...","email":"...","provisioningParameters":{"firstName":"...","lastName":"...","primaryPhoneNumber":"...","extension":"...","locationId":"..."},"package":"webex_calling","packages":["webex_calling"]}'."""
|
|
330
|
+
api = get_api(debug=debug)
|
|
331
|
+
url = f"https://webexapis.com/v1/wholesale/subscribers"
|
|
332
|
+
params = {}
|
|
333
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
334
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
335
|
+
if json_body:
|
|
336
|
+
body = json.loads(json_body)
|
|
337
|
+
else:
|
|
338
|
+
body = {}
|
|
339
|
+
if customer_id is not None:
|
|
340
|
+
body["customerId"] = customer_id
|
|
341
|
+
if email is not None:
|
|
342
|
+
body["email"] = email
|
|
343
|
+
if package is not None:
|
|
344
|
+
body["package"] = package
|
|
345
|
+
_missing = [f for f in ['customerId', 'email'] if f not in body or body[f] is None]
|
|
346
|
+
if _missing:
|
|
347
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
348
|
+
raise typer.Exit(1)
|
|
349
|
+
try:
|
|
350
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
351
|
+
except WebexError as e:
|
|
352
|
+
handle_rest_error(e)
|
|
353
|
+
if output == "json":
|
|
354
|
+
print_json(result)
|
|
355
|
+
elif isinstance(result, dict) and "id" in result:
|
|
356
|
+
typer.echo(f"Created: {result['id']}")
|
|
357
|
+
elif not result or result == {}:
|
|
358
|
+
typer.echo("Created.")
|
|
359
|
+
else:
|
|
360
|
+
print_json(result)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
@app.command("show-subscribers")
|
|
365
|
+
def show_subscribers(
|
|
366
|
+
subscriber_id: str = typer.Argument(help="subscriberId"),
|
|
367
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
368
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
369
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
370
|
+
):
|
|
371
|
+
"""Get a Wholesale Subscriber."""
|
|
372
|
+
api = get_api(debug=debug)
|
|
373
|
+
url = f"https://webexapis.com/v1/wholesale/subscribers/{subscriber_id}"
|
|
374
|
+
params = {}
|
|
375
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
376
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
377
|
+
try:
|
|
378
|
+
result = api.session.rest_get(url, params=params)
|
|
379
|
+
except WebexError as e:
|
|
380
|
+
handle_rest_error(e)
|
|
381
|
+
if output == "json":
|
|
382
|
+
print_json(result)
|
|
383
|
+
else:
|
|
384
|
+
if isinstance(result, dict):
|
|
385
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
386
|
+
elif isinstance(result, list):
|
|
387
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
388
|
+
else:
|
|
389
|
+
print_json(result)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
@app.command("update-subscribers")
|
|
394
|
+
def update_subscribers(
|
|
395
|
+
subscriber_id: str = typer.Argument(help="subscriberId"),
|
|
396
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
397
|
+
package: str = typer.Option(None, "--package", help="Choices: webex_calling, webex_meetings, webex_suite, webex_voice, cx_essentials, webex_calling_standard, attendant_console, cx_premium_agent, cx_standard_agent"),
|
|
398
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
399
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
400
|
+
):
|
|
401
|
+
"""Update a Wholesale Subscriber\n\nExample --json-body:\n '{"package":"webex_calling","packages":["webex_calling"],"provisioningParameters":{"primaryPhoneNumber":"...","extension":"...","locationId":"..."}}'."""
|
|
402
|
+
api = get_api(debug=debug)
|
|
403
|
+
url = f"https://webexapis.com/v1/wholesale/subscribers/{subscriber_id}"
|
|
404
|
+
params = {}
|
|
405
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
406
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
407
|
+
if json_body:
|
|
408
|
+
body = json.loads(json_body)
|
|
409
|
+
else:
|
|
410
|
+
body = {}
|
|
411
|
+
if package is not None:
|
|
412
|
+
body["package"] = package
|
|
413
|
+
try:
|
|
414
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
415
|
+
except WebexError as e:
|
|
416
|
+
handle_rest_error(e)
|
|
417
|
+
typer.echo(f"Updated.")
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@app.command("delete-subscribers")
|
|
422
|
+
def delete_subscribers(
|
|
423
|
+
subscriber_id: str = typer.Argument(help="subscriberId"),
|
|
424
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
425
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
426
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
427
|
+
):
|
|
428
|
+
"""Remove a Wholesale Subscriber."""
|
|
429
|
+
if not force:
|
|
430
|
+
typer.confirm(f"Delete {subscriber_id}?", abort=True)
|
|
431
|
+
api = get_api(debug=debug)
|
|
432
|
+
url = f"https://webexapis.com/v1/wholesale/subscribers/{subscriber_id}"
|
|
433
|
+
params = {}
|
|
434
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
435
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
436
|
+
try:
|
|
437
|
+
api.session.rest_delete(url, params=params)
|
|
438
|
+
except WebexError as e:
|
|
439
|
+
handle_rest_error(e)
|
|
440
|
+
typer.echo(f"Deleted: {subscriber_id}")
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@app.command("create-validate-subscribers")
|
|
445
|
+
def create_validate_subscribers(
|
|
446
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
447
|
+
provisioning_id: str = typer.Option(None, "--provisioning-id", help="Defines how this wholesale subscriber is to be provisioned f"),
|
|
448
|
+
customer_id: str = typer.Option(None, "--customer-id", help="ID of the Provisioned Customer for Webex Wholesale."),
|
|
449
|
+
email: str = typer.Option(None, "--email", help="(required) The email address of the subscriber."),
|
|
450
|
+
package: str = typer.Option(None, "--package", help="Choices: webex_calling, webex_meetings, webex_suite, webex_voice, cx_essentials, webex_calling_standard"),
|
|
451
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
452
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
453
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
454
|
+
):
|
|
455
|
+
"""Precheck a Wholesale Subscriber Provisioning\n\nExample --json-body:\n '{"email":"...","provisioningId":"...","customerId":"...","package":"webex_calling","packages":["webex_calling"],"provisioningParameters":{"firstName":"...","lastName":"...","primaryPhoneNumber":"...","extension":"...","locationId":"..."},"customerInfo":{"primaryEmail":"..."}}'."""
|
|
456
|
+
api = get_api(debug=debug)
|
|
457
|
+
url = f"https://webexapis.com/v1/wholesale/subscribers/validate"
|
|
458
|
+
params = {}
|
|
459
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
460
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
461
|
+
if json_body:
|
|
462
|
+
body = json.loads(json_body)
|
|
463
|
+
else:
|
|
464
|
+
body = {}
|
|
465
|
+
if provisioning_id is not None:
|
|
466
|
+
body["provisioningId"] = provisioning_id
|
|
467
|
+
if customer_id is not None:
|
|
468
|
+
body["customerId"] = customer_id
|
|
469
|
+
if email is not None:
|
|
470
|
+
body["email"] = email
|
|
471
|
+
if package is not None:
|
|
472
|
+
body["package"] = package
|
|
473
|
+
_missing = [f for f in ['email'] if f not in body or body[f] is None]
|
|
474
|
+
if _missing:
|
|
475
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
476
|
+
raise typer.Exit(1)
|
|
477
|
+
try:
|
|
478
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
479
|
+
except WebexError as e:
|
|
480
|
+
handle_rest_error(e)
|
|
481
|
+
if output == "json":
|
|
482
|
+
print_json(result)
|
|
483
|
+
elif isinstance(result, dict) and "id" in result:
|
|
484
|
+
typer.echo(f"Created: {result['id']}")
|
|
485
|
+
elif not result or result == {}:
|
|
486
|
+
typer.echo("Created.")
|
|
487
|
+
else:
|
|
488
|
+
print_json(result)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@app.command("create-consent-move")
|
|
493
|
+
def create_consent_move(
|
|
494
|
+
subscriber_id: str = typer.Argument(help="subscriberId"),
|
|
495
|
+
on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
|
|
496
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
497
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
498
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
499
|
+
):
|
|
500
|
+
"""Send Consent User Move Email to Pending Wholesale Subscribers."""
|
|
501
|
+
api = get_api(debug=debug)
|
|
502
|
+
url = f"https://webexapis.com/v1/subscribers/{subscriber_id}/emails/consentMove"
|
|
503
|
+
params = {}
|
|
504
|
+
if on_behalf_of_sub_partner_org_id is not None:
|
|
505
|
+
params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
|
|
506
|
+
if json_body:
|
|
507
|
+
body = json.loads(json_body)
|
|
508
|
+
else:
|
|
509
|
+
body = {}
|
|
510
|
+
try:
|
|
511
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
512
|
+
except WebexError as e:
|
|
513
|
+
handle_rest_error(e)
|
|
514
|
+
if output == "json":
|
|
515
|
+
print_json(result)
|
|
516
|
+
elif isinstance(result, dict) and "id" in result:
|
|
517
|
+
typer.echo(f"Created: {result['id']}")
|
|
518
|
+
elif not result or result == {}:
|
|
519
|
+
typer.echo("Created.")
|
|
520
|
+
else:
|
|
521
|
+
print_json(result)
|
|
522
|
+
|
|
523
|
+
|