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,787 @@
|
|
|
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 dect-devices.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("create")
|
|
13
|
+
def create(
|
|
14
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
15
|
+
name: str = typer.Option(None, "--name", help="(required) Name of the DECT network. Min and max length supported for t"),
|
|
16
|
+
display_name: str = typer.Option(None, "--display-name", help="Add a default name (11 characters max) to display for all ha"),
|
|
17
|
+
model: str = typer.Option(None, "--model", help="(required) Choices: DMS Cisco DBS110, Cisco DECT 110 Base, DMS Cisco DBS210, Cisco DECT 210 Base"),
|
|
18
|
+
default_access_code_enabled: bool = typer.Option(None, "--default-access-code-enabled/--no-default-access-code-enabled", help="(required) If set to true, need to provide a default access code that w"),
|
|
19
|
+
default_access_code: str = typer.Option(None, "--default-access-code", help="(required) If `defaultAccessCodeEnabled` is set to true, then provide a"),
|
|
20
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
21
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
22
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
23
|
+
):
|
|
24
|
+
"""Create a DECT Network."""
|
|
25
|
+
api = get_api(debug=debug)
|
|
26
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks"
|
|
27
|
+
params = {}
|
|
28
|
+
org_id = get_org_id()
|
|
29
|
+
if org_id is not None:
|
|
30
|
+
params["orgId"] = org_id
|
|
31
|
+
if json_body:
|
|
32
|
+
body = json.loads(json_body)
|
|
33
|
+
else:
|
|
34
|
+
body = {}
|
|
35
|
+
if name is not None:
|
|
36
|
+
body["name"] = name
|
|
37
|
+
if display_name is not None:
|
|
38
|
+
body["displayName"] = display_name
|
|
39
|
+
if model is not None:
|
|
40
|
+
body["model"] = model
|
|
41
|
+
if default_access_code_enabled is not None:
|
|
42
|
+
body["defaultAccessCodeEnabled"] = default_access_code_enabled
|
|
43
|
+
if default_access_code is not None:
|
|
44
|
+
body["defaultAccessCode"] = default_access_code
|
|
45
|
+
_missing = [f for f in ['name', 'model', 'defaultAccessCodeEnabled', 'defaultAccessCode'] if f not in body or body[f] is None]
|
|
46
|
+
if _missing:
|
|
47
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
48
|
+
raise typer.Exit(1)
|
|
49
|
+
try:
|
|
50
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
51
|
+
except WebexError as e:
|
|
52
|
+
handle_rest_error(e)
|
|
53
|
+
if output == "json":
|
|
54
|
+
print_json(result)
|
|
55
|
+
elif isinstance(result, dict) and "dectNetworkId" in result:
|
|
56
|
+
typer.echo(f"Created: {result['dectNetworkId']}")
|
|
57
|
+
elif isinstance(result, dict) and "id" in result:
|
|
58
|
+
typer.echo(f"Created: {result['id']}")
|
|
59
|
+
elif not result or result == {}:
|
|
60
|
+
typer.echo("Created.")
|
|
61
|
+
else:
|
|
62
|
+
print_json(result)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@app.command("list")
|
|
67
|
+
def cmd_list(
|
|
68
|
+
name: str = typer.Option(None, "--name", help="List of DECT networks with this name."),
|
|
69
|
+
location_id: str = typer.Option(None, "--location-id", help="List of DECT networks at this location."),
|
|
70
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
71
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
72
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
73
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
74
|
+
):
|
|
75
|
+
"""Get the List of DECT Networks for an organization."""
|
|
76
|
+
api = get_api(debug=debug)
|
|
77
|
+
url = f"https://webexapis.com/v1/telephony/config/dectNetworks"
|
|
78
|
+
params = {}
|
|
79
|
+
if name is not None:
|
|
80
|
+
params["name"] = name
|
|
81
|
+
if location_id is not None:
|
|
82
|
+
params["locationId"] = location_id
|
|
83
|
+
if limit > 0:
|
|
84
|
+
params["max"] = limit
|
|
85
|
+
if offset > 0:
|
|
86
|
+
params["start"] = offset
|
|
87
|
+
org_id = get_org_id()
|
|
88
|
+
if org_id is not None:
|
|
89
|
+
params["orgId"] = org_id
|
|
90
|
+
result = None
|
|
91
|
+
try:
|
|
92
|
+
result = api.session.rest_get(url, params=params)
|
|
93
|
+
except WebexError as e:
|
|
94
|
+
handle_rest_error(e)
|
|
95
|
+
result = result or []
|
|
96
|
+
items = result.get("dectNetworks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
97
|
+
if output == "json":
|
|
98
|
+
print_json(items)
|
|
99
|
+
else:
|
|
100
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@app.command("show")
|
|
105
|
+
def show(
|
|
106
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
107
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
108
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
109
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
110
|
+
):
|
|
111
|
+
"""Get DECT Network Details."""
|
|
112
|
+
api = get_api(debug=debug)
|
|
113
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}"
|
|
114
|
+
params = {}
|
|
115
|
+
org_id = get_org_id()
|
|
116
|
+
if org_id is not None:
|
|
117
|
+
params["orgId"] = org_id
|
|
118
|
+
try:
|
|
119
|
+
result = api.session.rest_get(url, params=params)
|
|
120
|
+
except WebexError as e:
|
|
121
|
+
handle_rest_error(e)
|
|
122
|
+
if output == "json":
|
|
123
|
+
print_json(result)
|
|
124
|
+
else:
|
|
125
|
+
if isinstance(result, dict):
|
|
126
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
127
|
+
elif isinstance(result, list):
|
|
128
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
129
|
+
else:
|
|
130
|
+
print_json(result)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@app.command("update")
|
|
135
|
+
def update(
|
|
136
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
137
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
138
|
+
name: str = typer.Option(None, "--name", help="Name of the DECT network. This should be unique across the l"),
|
|
139
|
+
display_name: str = typer.Option(None, "--display-name", help="DECT network name that will be displayed on the handset."),
|
|
140
|
+
default_access_code_enabled: bool = typer.Option(None, "--default-access-code-enabled/--no-default-access-code-enabled", help="Default access code is enabled. If true, the default access"),
|
|
141
|
+
default_access_code: str = typer.Option(None, "--default-access-code", help="Default access code for the DECT network. The default access"),
|
|
142
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
143
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
144
|
+
):
|
|
145
|
+
"""Update DECT Network."""
|
|
146
|
+
api = get_api(debug=debug)
|
|
147
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}"
|
|
148
|
+
params = {}
|
|
149
|
+
org_id = get_org_id()
|
|
150
|
+
if org_id is not None:
|
|
151
|
+
params["orgId"] = org_id
|
|
152
|
+
if json_body:
|
|
153
|
+
body = json.loads(json_body)
|
|
154
|
+
else:
|
|
155
|
+
body = {}
|
|
156
|
+
if name is not None:
|
|
157
|
+
body["name"] = name
|
|
158
|
+
if display_name is not None:
|
|
159
|
+
body["displayName"] = display_name
|
|
160
|
+
if default_access_code_enabled is not None:
|
|
161
|
+
body["defaultAccessCodeEnabled"] = default_access_code_enabled
|
|
162
|
+
if default_access_code is not None:
|
|
163
|
+
body["defaultAccessCode"] = default_access_code
|
|
164
|
+
try:
|
|
165
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
166
|
+
except WebexError as e:
|
|
167
|
+
handle_rest_error(e)
|
|
168
|
+
typer.echo(f"Updated.")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@app.command("delete")
|
|
173
|
+
def delete(
|
|
174
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
175
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
176
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
177
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
178
|
+
):
|
|
179
|
+
"""Delete DECT Network."""
|
|
180
|
+
if not force:
|
|
181
|
+
typer.confirm(f"Delete {dect_network_id}?", abort=True)
|
|
182
|
+
api = get_api(debug=debug)
|
|
183
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}"
|
|
184
|
+
params = {}
|
|
185
|
+
org_id = get_org_id()
|
|
186
|
+
if org_id is not None:
|
|
187
|
+
params["orgId"] = org_id
|
|
188
|
+
try:
|
|
189
|
+
api.session.rest_delete(url, params=params)
|
|
190
|
+
except WebexError as e:
|
|
191
|
+
handle_rest_error(e)
|
|
192
|
+
typer.echo(f"Deleted: {dect_network_id}")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@app.command("list-base-stations")
|
|
197
|
+
def list_base_stations(
|
|
198
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
199
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
200
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
201
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
202
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
203
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
204
|
+
):
|
|
205
|
+
"""Get a list of DECT Network Base Stations."""
|
|
206
|
+
api = get_api(debug=debug)
|
|
207
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations"
|
|
208
|
+
params = {}
|
|
209
|
+
if limit > 0:
|
|
210
|
+
params["max"] = limit
|
|
211
|
+
if offset > 0:
|
|
212
|
+
params["start"] = offset
|
|
213
|
+
org_id = get_org_id()
|
|
214
|
+
if org_id is not None:
|
|
215
|
+
params["orgId"] = org_id
|
|
216
|
+
result = None
|
|
217
|
+
try:
|
|
218
|
+
result = api.session.rest_get(url, params=params)
|
|
219
|
+
except WebexError as e:
|
|
220
|
+
handle_rest_error(e)
|
|
221
|
+
result = result or []
|
|
222
|
+
items = result.get("baseStations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
223
|
+
if output == "json":
|
|
224
|
+
print_json(items)
|
|
225
|
+
else:
|
|
226
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@app.command("create-base-stations")
|
|
231
|
+
def create_base_stations(
|
|
232
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
233
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
234
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
235
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
236
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
237
|
+
):
|
|
238
|
+
"""Create Multiple Base Stations\n\nExample --json-body:\n '{"baseStationMacs":["..."]}'."""
|
|
239
|
+
api = get_api(debug=debug)
|
|
240
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations"
|
|
241
|
+
params = {}
|
|
242
|
+
org_id = get_org_id()
|
|
243
|
+
if org_id is not None:
|
|
244
|
+
params["orgId"] = org_id
|
|
245
|
+
if json_body:
|
|
246
|
+
body = json.loads(json_body)
|
|
247
|
+
else:
|
|
248
|
+
body = {}
|
|
249
|
+
try:
|
|
250
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
251
|
+
except WebexError as e:
|
|
252
|
+
handle_rest_error(e)
|
|
253
|
+
if output == "json":
|
|
254
|
+
print_json(result)
|
|
255
|
+
elif isinstance(result, dict) and "baseStations" in result:
|
|
256
|
+
typer.echo(f"Created: {result['baseStations']}")
|
|
257
|
+
elif isinstance(result, dict) and "id" in result:
|
|
258
|
+
typer.echo(f"Created: {result['id']}")
|
|
259
|
+
elif not result or result == {}:
|
|
260
|
+
typer.echo("Created.")
|
|
261
|
+
else:
|
|
262
|
+
print_json(result)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@app.command("delete-base-stations-dect-networks")
|
|
267
|
+
def delete_base_stations_dect_networks(
|
|
268
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
269
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
270
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
271
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
272
|
+
):
|
|
273
|
+
"""Delete bulk DECT Network Base Stations."""
|
|
274
|
+
if not force:
|
|
275
|
+
typer.confirm(f"Delete {dect_network_id}?", abort=True)
|
|
276
|
+
api = get_api(debug=debug)
|
|
277
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations"
|
|
278
|
+
params = {}
|
|
279
|
+
org_id = get_org_id()
|
|
280
|
+
if org_id is not None:
|
|
281
|
+
params["orgId"] = org_id
|
|
282
|
+
try:
|
|
283
|
+
api.session.rest_delete(url, params=params)
|
|
284
|
+
except WebexError as e:
|
|
285
|
+
handle_rest_error(e)
|
|
286
|
+
typer.echo(f"Deleted: {dect_network_id}")
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@app.command("show-base-stations")
|
|
291
|
+
def show_base_stations(
|
|
292
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
293
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
294
|
+
base_station_id: str = typer.Argument(help="baseStationId"),
|
|
295
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
296
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
297
|
+
):
|
|
298
|
+
"""Get the details of a specific DECT Network Base Station."""
|
|
299
|
+
api = get_api(debug=debug)
|
|
300
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations/{base_station_id}"
|
|
301
|
+
params = {}
|
|
302
|
+
org_id = get_org_id()
|
|
303
|
+
if org_id is not None:
|
|
304
|
+
params["orgId"] = org_id
|
|
305
|
+
try:
|
|
306
|
+
result = api.session.rest_get(url, params=params)
|
|
307
|
+
except WebexError as e:
|
|
308
|
+
handle_rest_error(e)
|
|
309
|
+
if output == "json":
|
|
310
|
+
print_json(result)
|
|
311
|
+
else:
|
|
312
|
+
if isinstance(result, dict):
|
|
313
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
314
|
+
elif isinstance(result, list):
|
|
315
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
316
|
+
else:
|
|
317
|
+
print_json(result)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@app.command("delete-base-stations-dect-networks-1")
|
|
322
|
+
def delete_base_stations_dect_networks_1(
|
|
323
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
324
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
325
|
+
base_station_id: str = typer.Argument(help="baseStationId"),
|
|
326
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
327
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
328
|
+
):
|
|
329
|
+
"""Delete a specific DECT Network Base Station."""
|
|
330
|
+
if not force:
|
|
331
|
+
typer.confirm(f"Delete {base_station_id}?", abort=True)
|
|
332
|
+
api = get_api(debug=debug)
|
|
333
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations/{base_station_id}"
|
|
334
|
+
params = {}
|
|
335
|
+
org_id = get_org_id()
|
|
336
|
+
if org_id is not None:
|
|
337
|
+
params["orgId"] = org_id
|
|
338
|
+
try:
|
|
339
|
+
api.session.rest_delete(url, params=params)
|
|
340
|
+
except WebexError as e:
|
|
341
|
+
handle_rest_error(e)
|
|
342
|
+
typer.echo(f"Deleted: {base_station_id}")
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@app.command("list-handsets")
|
|
347
|
+
def list_handsets(
|
|
348
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
349
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
350
|
+
basestation_id: str = typer.Option(None, "--basestation-id", help="Search handset details in the specified DECT base station ID"),
|
|
351
|
+
member_id: str = typer.Option(None, "--member-id", help="ID of the member of the handset. Members can be of type PEOP"),
|
|
352
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
353
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
354
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
355
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
356
|
+
):
|
|
357
|
+
"""Get List of Handsets for a DECT Network ID."""
|
|
358
|
+
api = get_api(debug=debug)
|
|
359
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets"
|
|
360
|
+
params = {}
|
|
361
|
+
if basestation_id is not None:
|
|
362
|
+
params["basestationId"] = basestation_id
|
|
363
|
+
if member_id is not None:
|
|
364
|
+
params["memberId"] = member_id
|
|
365
|
+
if limit > 0:
|
|
366
|
+
params["max"] = limit
|
|
367
|
+
if offset > 0:
|
|
368
|
+
params["start"] = offset
|
|
369
|
+
org_id = get_org_id()
|
|
370
|
+
if org_id is not None:
|
|
371
|
+
params["orgId"] = org_id
|
|
372
|
+
result = None
|
|
373
|
+
try:
|
|
374
|
+
result = api.session.rest_get(url, params=params)
|
|
375
|
+
except WebexError as e:
|
|
376
|
+
handle_rest_error(e)
|
|
377
|
+
result = result or []
|
|
378
|
+
items = result.get("handsets", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
379
|
+
if output == "json":
|
|
380
|
+
print_json(items)
|
|
381
|
+
else:
|
|
382
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
@app.command("create-handsets")
|
|
387
|
+
def create_handsets(
|
|
388
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
389
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
390
|
+
line1_member_id: str = typer.Option(None, "--line1-member-id", help="(required) ID of the member on line1 of the handset. Members can be PEO"),
|
|
391
|
+
line2_member_id: str = typer.Option(None, "--line2-member-id", help="ID of the member on line2 of the handset. Members can be PEO"),
|
|
392
|
+
custom_display_name: str = typer.Option(None, "--custom-display-name", help="(required) Custom display name on the handset. Min and max length suppo"),
|
|
393
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
394
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
395
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
396
|
+
):
|
|
397
|
+
"""Add a Handset to a DECT Network."""
|
|
398
|
+
api = get_api(debug=debug)
|
|
399
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets"
|
|
400
|
+
params = {}
|
|
401
|
+
org_id = get_org_id()
|
|
402
|
+
if org_id is not None:
|
|
403
|
+
params["orgId"] = org_id
|
|
404
|
+
if json_body:
|
|
405
|
+
body = json.loads(json_body)
|
|
406
|
+
else:
|
|
407
|
+
body = {}
|
|
408
|
+
if line1_member_id is not None:
|
|
409
|
+
body["line1MemberId"] = line1_member_id
|
|
410
|
+
if line2_member_id is not None:
|
|
411
|
+
body["line2MemberId"] = line2_member_id
|
|
412
|
+
if custom_display_name is not None:
|
|
413
|
+
body["customDisplayName"] = custom_display_name
|
|
414
|
+
_missing = [f for f in ['line1MemberId', 'customDisplayName'] if f not in body or body[f] is None]
|
|
415
|
+
if _missing:
|
|
416
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
417
|
+
raise typer.Exit(1)
|
|
418
|
+
try:
|
|
419
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
420
|
+
except WebexError as e:
|
|
421
|
+
handle_rest_error(e)
|
|
422
|
+
if output == "json":
|
|
423
|
+
print_json(result)
|
|
424
|
+
elif isinstance(result, dict) and "id" in result:
|
|
425
|
+
typer.echo(f"Created: {result['id']}")
|
|
426
|
+
elif not result or result == {}:
|
|
427
|
+
typer.echo("Created.")
|
|
428
|
+
else:
|
|
429
|
+
print_json(result)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
@app.command("show-handsets")
|
|
434
|
+
def show_handsets(
|
|
435
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
436
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
437
|
+
handset_id: str = typer.Argument(help="handsetId"),
|
|
438
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
439
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
440
|
+
):
|
|
441
|
+
"""Get Specific DECT Network Handset Details."""
|
|
442
|
+
api = get_api(debug=debug)
|
|
443
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}"
|
|
444
|
+
params = {}
|
|
445
|
+
org_id = get_org_id()
|
|
446
|
+
if org_id is not None:
|
|
447
|
+
params["orgId"] = org_id
|
|
448
|
+
try:
|
|
449
|
+
result = api.session.rest_get(url, params=params)
|
|
450
|
+
except WebexError as e:
|
|
451
|
+
handle_rest_error(e)
|
|
452
|
+
if output == "json":
|
|
453
|
+
print_json(result)
|
|
454
|
+
else:
|
|
455
|
+
if isinstance(result, dict):
|
|
456
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
457
|
+
elif isinstance(result, list):
|
|
458
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
459
|
+
else:
|
|
460
|
+
print_json(result)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
@app.command("update-handsets")
|
|
465
|
+
def update_handsets(
|
|
466
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
467
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
468
|
+
handset_id: str = typer.Argument(help="handsetId"),
|
|
469
|
+
line1_member_id: str = typer.Option(None, "--line1-member-id", help="ID of the member on line1 of the handset. Members can be PEO"),
|
|
470
|
+
line2_member_id: str = typer.Option(None, "--line2-member-id", help="ID of the member on line2 of the handset. Members can be PEO"),
|
|
471
|
+
custom_display_name: str = typer.Option(None, "--custom-display-name", help="Custom display name on the handset."),
|
|
472
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
473
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
474
|
+
):
|
|
475
|
+
"""Update DECT Network Handset."""
|
|
476
|
+
api = get_api(debug=debug)
|
|
477
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}"
|
|
478
|
+
params = {}
|
|
479
|
+
org_id = get_org_id()
|
|
480
|
+
if org_id is not None:
|
|
481
|
+
params["orgId"] = org_id
|
|
482
|
+
if json_body:
|
|
483
|
+
body = json.loads(json_body)
|
|
484
|
+
else:
|
|
485
|
+
body = {}
|
|
486
|
+
if line1_member_id is not None:
|
|
487
|
+
body["line1MemberId"] = line1_member_id
|
|
488
|
+
if line2_member_id is not None:
|
|
489
|
+
body["line2MemberId"] = line2_member_id
|
|
490
|
+
if custom_display_name is not None:
|
|
491
|
+
body["customDisplayName"] = custom_display_name
|
|
492
|
+
try:
|
|
493
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
494
|
+
except WebexError as e:
|
|
495
|
+
handle_rest_error(e)
|
|
496
|
+
typer.echo(f"Updated.")
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
@app.command("delete-handsets-dect-networks")
|
|
501
|
+
def delete_handsets_dect_networks(
|
|
502
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
503
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
504
|
+
handset_id: str = typer.Argument(help="handsetId"),
|
|
505
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
506
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
507
|
+
):
|
|
508
|
+
"""Delete specific DECT Network Handset Details."""
|
|
509
|
+
if not force:
|
|
510
|
+
typer.confirm(f"Delete {handset_id}?", abort=True)
|
|
511
|
+
api = get_api(debug=debug)
|
|
512
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}"
|
|
513
|
+
params = {}
|
|
514
|
+
org_id = get_org_id()
|
|
515
|
+
if org_id is not None:
|
|
516
|
+
params["orgId"] = org_id
|
|
517
|
+
try:
|
|
518
|
+
api.session.rest_delete(url, params=params)
|
|
519
|
+
except WebexError as e:
|
|
520
|
+
handle_rest_error(e)
|
|
521
|
+
typer.echo(f"Deleted: {handset_id}")
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@app.command("delete-handsets-dect-networks-1")
|
|
526
|
+
def delete_handsets_dect_networks_1(
|
|
527
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
528
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
529
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
530
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
531
|
+
):
|
|
532
|
+
"""Delete multiple handsets."""
|
|
533
|
+
if not force:
|
|
534
|
+
typer.confirm(f"Delete {dect_network_id}?", abort=True)
|
|
535
|
+
api = get_api(debug=debug)
|
|
536
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/"
|
|
537
|
+
params = {}
|
|
538
|
+
org_id = get_org_id()
|
|
539
|
+
if org_id is not None:
|
|
540
|
+
params["orgId"] = org_id
|
|
541
|
+
try:
|
|
542
|
+
api.session.rest_delete(url, params=params)
|
|
543
|
+
except WebexError as e:
|
|
544
|
+
handle_rest_error(e)
|
|
545
|
+
typer.echo(f"Deleted: {dect_network_id}")
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@app.command("create-bulk")
|
|
550
|
+
def create_bulk(
|
|
551
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
552
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
553
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
554
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
555
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
556
|
+
):
|
|
557
|
+
"""Add a List of Handsets to a DECT Network\n\nExample --json-body:\n '{"items":[{"line1MemberId":"...","customDisplayName":"...","line2MemberId":"..."}]}'."""
|
|
558
|
+
api = get_api(debug=debug)
|
|
559
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/bulk"
|
|
560
|
+
params = {}
|
|
561
|
+
org_id = get_org_id()
|
|
562
|
+
if org_id is not None:
|
|
563
|
+
params["orgId"] = org_id
|
|
564
|
+
if json_body:
|
|
565
|
+
body = json.loads(json_body)
|
|
566
|
+
else:
|
|
567
|
+
body = {}
|
|
568
|
+
try:
|
|
569
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
570
|
+
except WebexError as e:
|
|
571
|
+
handle_rest_error(e)
|
|
572
|
+
if output == "json":
|
|
573
|
+
print_json(result)
|
|
574
|
+
elif isinstance(result, dict) and "items" in result:
|
|
575
|
+
typer.echo(f"Created: {result['items']}")
|
|
576
|
+
elif isinstance(result, dict) and "id" in result:
|
|
577
|
+
typer.echo(f"Created: {result['id']}")
|
|
578
|
+
elif not result or result == {}:
|
|
579
|
+
typer.echo("Created.")
|
|
580
|
+
else:
|
|
581
|
+
print_json(result)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
@app.command("list-dect-networks-people")
|
|
586
|
+
def list_dect_networks_people(
|
|
587
|
+
person_id: str = typer.Argument(help="personId"),
|
|
588
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
589
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
590
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
591
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
592
|
+
):
|
|
593
|
+
"""GET List of DECT networks associated with a Person."""
|
|
594
|
+
api = get_api(debug=debug)
|
|
595
|
+
url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/dectNetworks"
|
|
596
|
+
params = {}
|
|
597
|
+
if limit > 0:
|
|
598
|
+
params["max"] = limit
|
|
599
|
+
if offset > 0:
|
|
600
|
+
params["start"] = offset
|
|
601
|
+
org_id = get_org_id()
|
|
602
|
+
if org_id is not None:
|
|
603
|
+
params["orgId"] = org_id
|
|
604
|
+
result = None
|
|
605
|
+
try:
|
|
606
|
+
result = api.session.rest_get(url, params=params)
|
|
607
|
+
except WebexError as e:
|
|
608
|
+
handle_rest_error(e)
|
|
609
|
+
result = result or []
|
|
610
|
+
items = result.get("dectNetworks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
611
|
+
if output == "json":
|
|
612
|
+
print_json(items)
|
|
613
|
+
else:
|
|
614
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
@app.command("list-dect-networks-workspaces")
|
|
619
|
+
def list_dect_networks_workspaces(
|
|
620
|
+
workspace_id: str = typer.Argument(help="workspaceId"),
|
|
621
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
622
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
623
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
624
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
625
|
+
):
|
|
626
|
+
"""GET List of DECT networks associated with a workspace."""
|
|
627
|
+
api = get_api(debug=debug)
|
|
628
|
+
url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/dectNetworks"
|
|
629
|
+
params = {}
|
|
630
|
+
if limit > 0:
|
|
631
|
+
params["max"] = limit
|
|
632
|
+
if offset > 0:
|
|
633
|
+
params["start"] = offset
|
|
634
|
+
org_id = get_org_id()
|
|
635
|
+
if org_id is not None:
|
|
636
|
+
params["orgId"] = org_id
|
|
637
|
+
result = None
|
|
638
|
+
try:
|
|
639
|
+
result = api.session.rest_get(url, params=params)
|
|
640
|
+
except WebexError as e:
|
|
641
|
+
handle_rest_error(e)
|
|
642
|
+
result = result or []
|
|
643
|
+
items = result.get("dectNetworks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
644
|
+
if output == "json":
|
|
645
|
+
print_json(items)
|
|
646
|
+
else:
|
|
647
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
@app.command("list-available-members")
|
|
652
|
+
def list_available_members(
|
|
653
|
+
member_name: str = typer.Option(None, "--member-name", help="Search (Contains) numbers based on member name."),
|
|
654
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Search (Contains) based on number."),
|
|
655
|
+
extension: str = typer.Option(None, "--extension", help="Search (Contains) based on extension."),
|
|
656
|
+
order: str = typer.Option(None, "--order", help="Sort the list of available members on the device in ascendin"),
|
|
657
|
+
location_id: str = typer.Option(None, "--location-id", help="List members for the location ID."),
|
|
658
|
+
exclude_virtual_line: str = typer.Option(None, "--exclude-virtual-line", help="If true, search results will exclude virtual lines in the me"),
|
|
659
|
+
usage_type: str = typer.Option(None, "--usage-type", help="Choices: DEVICE_OWNER, SHARED_LINE"),
|
|
660
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
661
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
662
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
663
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
664
|
+
):
|
|
665
|
+
"""Search Available Members."""
|
|
666
|
+
api = get_api(debug=debug)
|
|
667
|
+
url = f"https://webexapis.com/v1/telephony/config/devices/availableMembers"
|
|
668
|
+
params = {}
|
|
669
|
+
if member_name is not None:
|
|
670
|
+
params["memberName"] = member_name
|
|
671
|
+
if phone_number is not None:
|
|
672
|
+
params["phoneNumber"] = phone_number
|
|
673
|
+
if extension is not None:
|
|
674
|
+
params["extension"] = extension
|
|
675
|
+
if order is not None:
|
|
676
|
+
params["order"] = order
|
|
677
|
+
if location_id is not None:
|
|
678
|
+
params["locationId"] = location_id
|
|
679
|
+
if exclude_virtual_line is not None:
|
|
680
|
+
params["excludeVirtualLine"] = exclude_virtual_line
|
|
681
|
+
if usage_type is not None:
|
|
682
|
+
params["usageType"] = usage_type
|
|
683
|
+
if limit > 0:
|
|
684
|
+
params["max"] = limit
|
|
685
|
+
if offset > 0:
|
|
686
|
+
params["start"] = offset
|
|
687
|
+
org_id = get_org_id()
|
|
688
|
+
if org_id is not None:
|
|
689
|
+
params["orgId"] = org_id
|
|
690
|
+
result = None
|
|
691
|
+
try:
|
|
692
|
+
result = api.session.rest_get(url, params=params)
|
|
693
|
+
except WebexError as e:
|
|
694
|
+
handle_rest_error(e)
|
|
695
|
+
result = result or []
|
|
696
|
+
items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
697
|
+
if output == "json":
|
|
698
|
+
print_json(items)
|
|
699
|
+
else:
|
|
700
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
@app.command("generate-and-enable")
|
|
705
|
+
def generate_and_enable(
|
|
706
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
707
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
708
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
|
|
709
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
710
|
+
):
|
|
711
|
+
"""Generate and Enable DECT Serviceability Password."""
|
|
712
|
+
api = get_api(debug=debug)
|
|
713
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword/actions/generate/invoke"
|
|
714
|
+
params = {}
|
|
715
|
+
org_id = get_org_id()
|
|
716
|
+
if org_id is not None:
|
|
717
|
+
params["orgId"] = org_id
|
|
718
|
+
if json_body:
|
|
719
|
+
body = json.loads(json_body)
|
|
720
|
+
else:
|
|
721
|
+
body = {}
|
|
722
|
+
try:
|
|
723
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
724
|
+
except WebexError as e:
|
|
725
|
+
handle_rest_error(e)
|
|
726
|
+
print_json(result)
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
@app.command("show-serviceability-password")
|
|
731
|
+
def show_serviceability_password(
|
|
732
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
733
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
734
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
735
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
736
|
+
):
|
|
737
|
+
"""Get DECT Serviceability Password status."""
|
|
738
|
+
api = get_api(debug=debug)
|
|
739
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword"
|
|
740
|
+
params = {}
|
|
741
|
+
org_id = get_org_id()
|
|
742
|
+
if org_id is not None:
|
|
743
|
+
params["orgId"] = org_id
|
|
744
|
+
try:
|
|
745
|
+
result = api.session.rest_get(url, params=params)
|
|
746
|
+
except WebexError as e:
|
|
747
|
+
handle_rest_error(e)
|
|
748
|
+
if output == "json":
|
|
749
|
+
print_json(result)
|
|
750
|
+
else:
|
|
751
|
+
if isinstance(result, dict):
|
|
752
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
753
|
+
elif isinstance(result, list):
|
|
754
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
755
|
+
else:
|
|
756
|
+
print_json(result)
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
@app.command("update-serviceability-password")
|
|
761
|
+
def update_serviceability_password(
|
|
762
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
763
|
+
dect_network_id: str = typer.Argument(help="dectNetworkId"),
|
|
764
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="DECT serviceability password status. When `enabled` is set t"),
|
|
765
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
766
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
767
|
+
):
|
|
768
|
+
"""Update DECT Serviceability Password status."""
|
|
769
|
+
api = get_api(debug=debug)
|
|
770
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword"
|
|
771
|
+
params = {}
|
|
772
|
+
org_id = get_org_id()
|
|
773
|
+
if org_id is not None:
|
|
774
|
+
params["orgId"] = org_id
|
|
775
|
+
if json_body:
|
|
776
|
+
body = json.loads(json_body)
|
|
777
|
+
else:
|
|
778
|
+
body = {}
|
|
779
|
+
if enabled is not None:
|
|
780
|
+
body["enabled"] = enabled
|
|
781
|
+
try:
|
|
782
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
783
|
+
except WebexError as e:
|
|
784
|
+
handle_rest_error(e)
|
|
785
|
+
typer.echo(f"Updated.")
|
|
786
|
+
|
|
787
|
+
|