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,46 @@
|
|
|
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 resolve_org_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage Webex Calling scim-bulk.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("create")
|
|
13
|
+
def create(
|
|
14
|
+
fail_on_errors: str = typer.Option(None, "--fail-on-errors", help="(required) An integer specifying the maximum number of errors that the"),
|
|
15
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
16
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
17
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
18
|
+
):
|
|
19
|
+
"""User bulk API\n\nExample --json-body:\n '{"schemas":["..."],"failOnErrors":0,"operations":[{"method":"...","path":"...","data":"...","bulkId":"..."}]}'."""
|
|
20
|
+
api = get_api(debug=debug)
|
|
21
|
+
org_id = resolve_org_id(api.session)
|
|
22
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Bulk"
|
|
23
|
+
if json_body:
|
|
24
|
+
body = json.loads(json_body)
|
|
25
|
+
else:
|
|
26
|
+
body = {}
|
|
27
|
+
if fail_on_errors is not None:
|
|
28
|
+
body["failOnErrors"] = fail_on_errors
|
|
29
|
+
_missing = [f for f in ['failOnErrors'] if f not in body or body[f] is None]
|
|
30
|
+
if _missing:
|
|
31
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
32
|
+
raise typer.Exit(1)
|
|
33
|
+
try:
|
|
34
|
+
result = api.session.rest_post(url, json=body)
|
|
35
|
+
except WebexError as e:
|
|
36
|
+
handle_rest_error(e)
|
|
37
|
+
if output == "json":
|
|
38
|
+
print_json(result)
|
|
39
|
+
elif isinstance(result, dict) and "id" in result:
|
|
40
|
+
typer.echo(f"Created: {result['id']}")
|
|
41
|
+
elif not result or result == {}:
|
|
42
|
+
typer.echo("Created.")
|
|
43
|
+
else:
|
|
44
|
+
print_json(result)
|
|
45
|
+
|
|
46
|
+
|
|
@@ -0,0 +1,246 @@
|
|
|
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 resolve_org_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage Webex Calling scim-groups.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
filter_param: str = typer.Option(None, "--filter", help="The url encoded filter. The example content is 'displayName"),
|
|
15
|
+
attributes: str = typer.Option(None, "--attributes", help="The attributes to return."),
|
|
16
|
+
excluded_attributes: str = typer.Option(None, "--excluded-attributes", help="Attributes to be excluded from the return."),
|
|
17
|
+
sort_by: str = typer.Option(None, "--sort-by", help="A string indicating the attribute whose value be used to ord"),
|
|
18
|
+
sort_order: str = typer.Option(None, "--sort-order", help="A string indicating the order in which the `sortBy` paramete"),
|
|
19
|
+
start_index: str = typer.Option(None, "--start-index", help="An integer indicating the 1-based index of the first query r"),
|
|
20
|
+
count: str = typer.Option(None, "--count", help="An integer indicating the desired maximum number of query re"),
|
|
21
|
+
include_members: str = typer.Option(None, "--include-members", help="Default \"false\". If false, no members returned."),
|
|
22
|
+
member_type: str = typer.Option(None, "--member-type", help="Filter the members by member type. Sample data: `user`, `mac"),
|
|
23
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
24
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
25
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
26
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
27
|
+
):
|
|
28
|
+
"""Search groups."""
|
|
29
|
+
api = get_api(debug=debug)
|
|
30
|
+
org_id = resolve_org_id(api.session)
|
|
31
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Groups"
|
|
32
|
+
params = {}
|
|
33
|
+
if filter_param is not None:
|
|
34
|
+
params["filter"] = filter_param
|
|
35
|
+
if attributes is not None:
|
|
36
|
+
params["attributes"] = attributes
|
|
37
|
+
if excluded_attributes is not None:
|
|
38
|
+
params["excludedAttributes"] = excluded_attributes
|
|
39
|
+
if sort_by is not None:
|
|
40
|
+
params["sortBy"] = sort_by
|
|
41
|
+
if sort_order is not None:
|
|
42
|
+
params["sortOrder"] = sort_order
|
|
43
|
+
if start_index is not None:
|
|
44
|
+
params["startIndex"] = start_index
|
|
45
|
+
if count is not None:
|
|
46
|
+
params["count"] = count
|
|
47
|
+
if include_members is not None:
|
|
48
|
+
params["includeMembers"] = include_members
|
|
49
|
+
if member_type is not None:
|
|
50
|
+
params["memberType"] = member_type
|
|
51
|
+
if limit > 0:
|
|
52
|
+
params["max"] = limit
|
|
53
|
+
if offset > 0:
|
|
54
|
+
params["start"] = offset
|
|
55
|
+
result = None
|
|
56
|
+
try:
|
|
57
|
+
result = api.session.rest_get(url, params=params)
|
|
58
|
+
except WebexError as e:
|
|
59
|
+
handle_rest_error(e)
|
|
60
|
+
result = result or []
|
|
61
|
+
items = result.get("Resources", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
62
|
+
if output == "json":
|
|
63
|
+
print_json(items)
|
|
64
|
+
else:
|
|
65
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.command("create")
|
|
70
|
+
def create(
|
|
71
|
+
display_name: str = typer.Option(None, "--display-name", help="(required) A human-readable name for the Group."),
|
|
72
|
+
external_id: str = typer.Option(None, "--external-id", help="An identifier for the resource as defined by the provisionin"),
|
|
73
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
74
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
75
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
76
|
+
):
|
|
77
|
+
"""Create a group\n\nExample --json-body:\n '{"schemas":["..."],"displayName":"...","externalId":"...","members":[{"value":"...","type":"..."}],"urn:scim:schemas:extension:cisco:webexidentity:2.0:Group":{"usage":"...","owners":["..."],"inheritances":["..."],"managedBy":["..."]}}'."""
|
|
78
|
+
api = get_api(debug=debug)
|
|
79
|
+
org_id = resolve_org_id(api.session)
|
|
80
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Groups"
|
|
81
|
+
if json_body:
|
|
82
|
+
body = json.loads(json_body)
|
|
83
|
+
else:
|
|
84
|
+
body = {}
|
|
85
|
+
if display_name is not None:
|
|
86
|
+
body["displayName"] = display_name
|
|
87
|
+
if external_id is not None:
|
|
88
|
+
body["externalId"] = external_id
|
|
89
|
+
_missing = [f for f in ['displayName'] if f not in body or body[f] is None]
|
|
90
|
+
if _missing:
|
|
91
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
92
|
+
raise typer.Exit(1)
|
|
93
|
+
try:
|
|
94
|
+
result = api.session.rest_post(url, json=body)
|
|
95
|
+
except WebexError as e:
|
|
96
|
+
handle_rest_error(e)
|
|
97
|
+
if output == "json":
|
|
98
|
+
print_json(result)
|
|
99
|
+
elif isinstance(result, dict) and "id" in result:
|
|
100
|
+
typer.echo(f"Created: {result['id']}")
|
|
101
|
+
elif not result or result == {}:
|
|
102
|
+
typer.echo("Created.")
|
|
103
|
+
else:
|
|
104
|
+
print_json(result)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@app.command("show")
|
|
109
|
+
def show(
|
|
110
|
+
group_id: str = typer.Argument(help="groupId"),
|
|
111
|
+
excluded_attributes: str = typer.Option(None, "--excluded-attributes", help="Attributes to be excluded from the return."),
|
|
112
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
113
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
114
|
+
):
|
|
115
|
+
"""Get a group."""
|
|
116
|
+
api = get_api(debug=debug)
|
|
117
|
+
org_id = resolve_org_id(api.session)
|
|
118
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Groups/{group_id}"
|
|
119
|
+
params = {}
|
|
120
|
+
if excluded_attributes is not None:
|
|
121
|
+
params["excludedAttributes"] = excluded_attributes
|
|
122
|
+
try:
|
|
123
|
+
result = api.session.rest_get(url, params=params)
|
|
124
|
+
except WebexError as e:
|
|
125
|
+
handle_rest_error(e)
|
|
126
|
+
if output == "json":
|
|
127
|
+
print_json(result)
|
|
128
|
+
else:
|
|
129
|
+
if isinstance(result, dict):
|
|
130
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
131
|
+
elif isinstance(result, list):
|
|
132
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
133
|
+
else:
|
|
134
|
+
print_json(result)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@app.command("update")
|
|
139
|
+
def update(
|
|
140
|
+
group_id: str = typer.Argument(help="groupId"),
|
|
141
|
+
display_name: str = typer.Option(None, "--display-name", help="A human-readable name for the group."),
|
|
142
|
+
external_id: str = typer.Option(None, "--external-id", help="An identifier for the resource as defined by the provisionin"),
|
|
143
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
144
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
145
|
+
):
|
|
146
|
+
"""Update a group with PUT\n\nExample --json-body:\n '{"schemas":["..."],"displayName":"...","externalId":"...","members":[{"value":"...","type":"..."}],"urn:scim:schemas:extension:cisco:webexidentity:2.0:Group":{"usage":"...","owners":["..."],"inheritances":["..."],"managedBy":["..."]}}'."""
|
|
147
|
+
api = get_api(debug=debug)
|
|
148
|
+
org_id = resolve_org_id(api.session)
|
|
149
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Groups/{group_id}"
|
|
150
|
+
if json_body:
|
|
151
|
+
body = json.loads(json_body)
|
|
152
|
+
else:
|
|
153
|
+
body = {}
|
|
154
|
+
if display_name is not None:
|
|
155
|
+
body["displayName"] = display_name
|
|
156
|
+
if external_id is not None:
|
|
157
|
+
body["externalId"] = external_id
|
|
158
|
+
try:
|
|
159
|
+
result = api.session.rest_put(url, json=body)
|
|
160
|
+
except WebexError as e:
|
|
161
|
+
handle_rest_error(e)
|
|
162
|
+
typer.echo(f"Updated.")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@app.command("update-groups")
|
|
167
|
+
def update_groups(
|
|
168
|
+
group_id: str = typer.Argument(help="groupId"),
|
|
169
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
170
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
171
|
+
):
|
|
172
|
+
"""Update a group with PATCH\n\nExample --json-body:\n '{"schemas":["..."],"Operations":[{"op":"...","path":"...","value":"..."}]}'."""
|
|
173
|
+
api = get_api(debug=debug)
|
|
174
|
+
org_id = resolve_org_id(api.session)
|
|
175
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Groups/{group_id}"
|
|
176
|
+
if json_body:
|
|
177
|
+
body = json.loads(json_body)
|
|
178
|
+
else:
|
|
179
|
+
body = {}
|
|
180
|
+
try:
|
|
181
|
+
result = api.session.rest_patch(url, json=body)
|
|
182
|
+
except WebexError as e:
|
|
183
|
+
handle_rest_error(e)
|
|
184
|
+
typer.echo(f"Updated.")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@app.command("delete")
|
|
189
|
+
def delete(
|
|
190
|
+
group_id: str = typer.Argument(help="groupId"),
|
|
191
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
192
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
193
|
+
):
|
|
194
|
+
"""Delete a group."""
|
|
195
|
+
if not force:
|
|
196
|
+
typer.confirm(f"Delete {group_id}?", abort=True)
|
|
197
|
+
api = get_api(debug=debug)
|
|
198
|
+
org_id = resolve_org_id(api.session)
|
|
199
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Groups/{group_id}"
|
|
200
|
+
try:
|
|
201
|
+
api.session.rest_delete(url)
|
|
202
|
+
except WebexError as e:
|
|
203
|
+
handle_rest_error(e)
|
|
204
|
+
typer.echo(f"Deleted: {group_id}")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@app.command("list-members")
|
|
209
|
+
def list_members(
|
|
210
|
+
group_id: str = typer.Argument(help="groupId"),
|
|
211
|
+
start_index: str = typer.Option(None, "--start-index", help="The index to start for group pagination."),
|
|
212
|
+
count: str = typer.Option(None, "--count", help="Non-negative integer that specifies the desired number of se"),
|
|
213
|
+
member_type: str = typer.Option(None, "--member-type", help="Filter the members by member type. Sample data: `user`, `mac"),
|
|
214
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
215
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
216
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
217
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
218
|
+
):
|
|
219
|
+
"""Get Group Members."""
|
|
220
|
+
api = get_api(debug=debug)
|
|
221
|
+
org_id = resolve_org_id(api.session)
|
|
222
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Groups/{group_id}/Members"
|
|
223
|
+
params = {}
|
|
224
|
+
if start_index is not None:
|
|
225
|
+
params["startIndex"] = start_index
|
|
226
|
+
if count is not None:
|
|
227
|
+
params["count"] = count
|
|
228
|
+
if member_type is not None:
|
|
229
|
+
params["memberType"] = member_type
|
|
230
|
+
if limit > 0:
|
|
231
|
+
params["max"] = limit
|
|
232
|
+
if offset > 0:
|
|
233
|
+
params["start"] = offset
|
|
234
|
+
result = None
|
|
235
|
+
try:
|
|
236
|
+
result = api.session.rest_get(url, params=params)
|
|
237
|
+
except WebexError as e:
|
|
238
|
+
handle_rest_error(e)
|
|
239
|
+
result = result or []
|
|
240
|
+
items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
241
|
+
if output == "json":
|
|
242
|
+
print_json(items)
|
|
243
|
+
else:
|
|
244
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
245
|
+
|
|
246
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from wxcli.auth import get_api
|
|
4
|
+
from wxcli.errors import WebexError, handle_rest_error
|
|
5
|
+
from wxcli.output import print_table, print_json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(help="Manage Webex Calling scim-schemas.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("show")
|
|
12
|
+
def show(
|
|
13
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
14
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
15
|
+
):
|
|
16
|
+
"""Get Group Schema."""
|
|
17
|
+
api = get_api(debug=debug)
|
|
18
|
+
url = f"https://webexapis.com/Schemas/SCIM2/Group"
|
|
19
|
+
try:
|
|
20
|
+
result = api.session.rest_get(url)
|
|
21
|
+
except WebexError as e:
|
|
22
|
+
handle_rest_error(e)
|
|
23
|
+
if output == "json":
|
|
24
|
+
print_json(result)
|
|
25
|
+
else:
|
|
26
|
+
if isinstance(result, dict):
|
|
27
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
28
|
+
elif isinstance(result, list):
|
|
29
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
30
|
+
else:
|
|
31
|
+
print_json(result)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.command("show-user")
|
|
36
|
+
def show_user(
|
|
37
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
38
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
39
|
+
):
|
|
40
|
+
"""Get User Schema."""
|
|
41
|
+
api = get_api(debug=debug)
|
|
42
|
+
url = f"https://webexapis.com/Schemas/SCIM2/User"
|
|
43
|
+
try:
|
|
44
|
+
result = api.session.rest_get(url)
|
|
45
|
+
except WebexError as e:
|
|
46
|
+
handle_rest_error(e)
|
|
47
|
+
if output == "json":
|
|
48
|
+
print_json(result)
|
|
49
|
+
else:
|
|
50
|
+
if isinstance(result, dict):
|
|
51
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
52
|
+
elif isinstance(result, list):
|
|
53
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
54
|
+
else:
|
|
55
|
+
print_json(result)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@app.command("show-scim2")
|
|
60
|
+
def show_scim2(
|
|
61
|
+
schema_id: str = typer.Argument(help="schemaId"),
|
|
62
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
63
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
64
|
+
):
|
|
65
|
+
"""Get Schema using Group Schema ID."""
|
|
66
|
+
api = get_api(debug=debug)
|
|
67
|
+
url = f"https://webexapis.com/Schemas/SCIM2/{schema_id}"
|
|
68
|
+
try:
|
|
69
|
+
result = api.session.rest_get(url)
|
|
70
|
+
except WebexError as e:
|
|
71
|
+
handle_rest_error(e)
|
|
72
|
+
if output == "json":
|
|
73
|
+
print_json(result)
|
|
74
|
+
else:
|
|
75
|
+
if isinstance(result, dict):
|
|
76
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
77
|
+
elif isinstance(result, list):
|
|
78
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
79
|
+
else:
|
|
80
|
+
print_json(result)
|
|
81
|
+
|
|
82
|
+
|
|
@@ -0,0 +1,283 @@
|
|
|
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 resolve_org_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage Webex Calling scim-users.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
filter_param: str = typer.Option(None, "--filter", help="The URL encoded filter. If the value is empty, the API will"),
|
|
15
|
+
attributes: str = typer.Option(None, "--attributes", help="A multi-valued list of string names for resource attributes"),
|
|
16
|
+
excluded_attributes: str = typer.Option(None, "--excluded-attributes", help="A multi-valued list of strings names for resource attributes"),
|
|
17
|
+
sort_by: str = typer.Option(None, "--sort-by", help="A string for the attribute whose value can be used to order"),
|
|
18
|
+
sort_order: str = typer.Option(None, "--sort-order", help="A string for the order in which the 'sortBy' parameter is ap"),
|
|
19
|
+
start_index: str = typer.Option(None, "--start-index", help="An integer for the 1-based index of the first query result."),
|
|
20
|
+
count: str = typer.Option(None, "--count", help="An integer for the maximum number of query results per page."),
|
|
21
|
+
return_groups: str = typer.Option(None, "--return-groups", help="Define whether the group information needs to be returned."),
|
|
22
|
+
include_group_details: str = typer.Option(None, "--include-group-details", help="Define whether the group information with details needs to b"),
|
|
23
|
+
group_usage_types: str = typer.Option(None, "--group-usage-types", help="Returns groups with details of the specified group type."),
|
|
24
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
25
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
26
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
27
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
28
|
+
):
|
|
29
|
+
"""Search users."""
|
|
30
|
+
api = get_api(debug=debug)
|
|
31
|
+
org_id = resolve_org_id(api.session)
|
|
32
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Users"
|
|
33
|
+
params = {}
|
|
34
|
+
if filter_param is not None:
|
|
35
|
+
params["filter"] = filter_param
|
|
36
|
+
if attributes is not None:
|
|
37
|
+
params["attributes"] = attributes
|
|
38
|
+
if excluded_attributes is not None:
|
|
39
|
+
params["excludedAttributes"] = excluded_attributes
|
|
40
|
+
if sort_by is not None:
|
|
41
|
+
params["sortBy"] = sort_by
|
|
42
|
+
if sort_order is not None:
|
|
43
|
+
params["sortOrder"] = sort_order
|
|
44
|
+
if start_index is not None:
|
|
45
|
+
params["startIndex"] = start_index
|
|
46
|
+
if count is not None:
|
|
47
|
+
params["count"] = count
|
|
48
|
+
if return_groups is not None:
|
|
49
|
+
params["returnGroups"] = return_groups
|
|
50
|
+
if include_group_details is not None:
|
|
51
|
+
params["includeGroupDetails"] = include_group_details
|
|
52
|
+
if group_usage_types is not None:
|
|
53
|
+
params["groupUsageTypes"] = group_usage_types
|
|
54
|
+
if limit > 0:
|
|
55
|
+
params["max"] = limit
|
|
56
|
+
if offset > 0:
|
|
57
|
+
params["start"] = offset
|
|
58
|
+
result = None
|
|
59
|
+
try:
|
|
60
|
+
result = api.session.rest_get(url, params=params)
|
|
61
|
+
except WebexError as e:
|
|
62
|
+
handle_rest_error(e)
|
|
63
|
+
result = result or []
|
|
64
|
+
items = result.get("Resources", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
65
|
+
if output == "json":
|
|
66
|
+
print_json(items)
|
|
67
|
+
else:
|
|
68
|
+
print_table(items, columns=[('ID', 'id'), ('Display Name', 'displayName'), ('User Name', 'userName'), ('Active', 'active')], limit=limit)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@app.command("create")
|
|
73
|
+
def create(
|
|
74
|
+
user_name: str = typer.Option(None, "--user-name", help="(required) A unique identifier for the user that authenticates the user"),
|
|
75
|
+
user_type: str = typer.Option(None, "--user-type", help="(required) Choices: user, room, external_calling, calling_service"),
|
|
76
|
+
title: str = typer.Option(None, "--title", help="The user's business title. Examples of a title is \"Business"),
|
|
77
|
+
active: bool = typer.Option(None, "--active/--no-active", help="A boolean value of \"true\" or \"false\" indicating whether the"),
|
|
78
|
+
preferred_language: str = typer.Option(None, "--preferred-language", help="User's preferred language. Acceptable values are based on th"),
|
|
79
|
+
locale: str = typer.Option(None, "--locale", help="The user's locale which represents the user's currency, time"),
|
|
80
|
+
timezone: str = typer.Option(None, "--timezone", help="The user's time zone specified in the [IANA timezone](https:"),
|
|
81
|
+
profile_url: str = typer.Option(None, "--profile-url", help="A fully qualified URL pointing to a page representing the us"),
|
|
82
|
+
external_id: str = typer.Option(None, "--external-id", help="User identifier provided by an external provisioning source."),
|
|
83
|
+
display_name: str = typer.Option(None, "--display-name", help="The name displayed for the user in Webex."),
|
|
84
|
+
nick_name: str = typer.Option(None, "--nick-name", help="A casual name of the user. For example, Bob when the user's"),
|
|
85
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
86
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
87
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
88
|
+
):
|
|
89
|
+
"""Create a user\n\nExample --json-body:\n '{"schemas":["..."],"userName":"...","userType":"user","title":"...","active":true,"roles":[{"value":"...","type":"...","display":"..."}],"preferredLanguage":"...","locale":"..."}'."""
|
|
90
|
+
api = get_api(debug=debug)
|
|
91
|
+
org_id = resolve_org_id(api.session)
|
|
92
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Users"
|
|
93
|
+
if json_body:
|
|
94
|
+
body = json.loads(json_body)
|
|
95
|
+
else:
|
|
96
|
+
body = {}
|
|
97
|
+
if user_name is not None:
|
|
98
|
+
body["userName"] = user_name
|
|
99
|
+
if user_type is not None:
|
|
100
|
+
body["userType"] = user_type
|
|
101
|
+
if title is not None:
|
|
102
|
+
body["title"] = title
|
|
103
|
+
if active is not None:
|
|
104
|
+
body["active"] = active
|
|
105
|
+
if preferred_language is not None:
|
|
106
|
+
body["preferredLanguage"] = preferred_language
|
|
107
|
+
if locale is not None:
|
|
108
|
+
body["locale"] = locale
|
|
109
|
+
if timezone is not None:
|
|
110
|
+
body["timezone"] = timezone
|
|
111
|
+
if profile_url is not None:
|
|
112
|
+
body["profileUrl"] = profile_url
|
|
113
|
+
if external_id is not None:
|
|
114
|
+
body["externalId"] = external_id
|
|
115
|
+
if display_name is not None:
|
|
116
|
+
body["displayName"] = display_name
|
|
117
|
+
if nick_name is not None:
|
|
118
|
+
body["nickName"] = nick_name
|
|
119
|
+
_missing = [f for f in ['userName', 'userType'] if f not in body or body[f] is None]
|
|
120
|
+
if _missing:
|
|
121
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
122
|
+
raise typer.Exit(1)
|
|
123
|
+
try:
|
|
124
|
+
result = api.session.rest_post(url, json=body)
|
|
125
|
+
except WebexError as e:
|
|
126
|
+
handle_rest_error(e)
|
|
127
|
+
if output == "json":
|
|
128
|
+
print_json(result)
|
|
129
|
+
elif isinstance(result, dict) and "id" in result:
|
|
130
|
+
typer.echo(f"Created: {result['id']}")
|
|
131
|
+
elif not result or result == {}:
|
|
132
|
+
typer.echo("Created.")
|
|
133
|
+
else:
|
|
134
|
+
print_json(result)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@app.command("show")
|
|
139
|
+
def show(
|
|
140
|
+
user_id: str = typer.Argument(help="userId"),
|
|
141
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
142
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
143
|
+
):
|
|
144
|
+
"""Get a user."""
|
|
145
|
+
api = get_api(debug=debug)
|
|
146
|
+
org_id = resolve_org_id(api.session)
|
|
147
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Users/{user_id}"
|
|
148
|
+
try:
|
|
149
|
+
result = api.session.rest_get(url)
|
|
150
|
+
except WebexError as e:
|
|
151
|
+
handle_rest_error(e)
|
|
152
|
+
if output == "json":
|
|
153
|
+
print_json(result)
|
|
154
|
+
else:
|
|
155
|
+
if isinstance(result, dict):
|
|
156
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
157
|
+
elif isinstance(result, list):
|
|
158
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
159
|
+
else:
|
|
160
|
+
print_json(result)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@app.command("update")
|
|
165
|
+
def update(
|
|
166
|
+
user_id: str = typer.Argument(help="userId"),
|
|
167
|
+
user_name: str = typer.Option(None, "--user-name", help="A unique identifier for the user and authenticates the user"),
|
|
168
|
+
user_type: str = typer.Option(None, "--user-type", help="Choices: user, room, external_calling, calling_service"),
|
|
169
|
+
title: str = typer.Option(None, "--title", help="The user's business title. Examples of a title is \"Business"),
|
|
170
|
+
active: bool = typer.Option(None, "--active/--no-active", help="A boolean value of \"true\" or \"false\" indicating whether the"),
|
|
171
|
+
preferred_language: str = typer.Option(None, "--preferred-language", help="User's preferred language. Acceptable values for this field"),
|
|
172
|
+
locale: str = typer.Option(None, "--locale", help="The user's locale which represents the user's currency, time"),
|
|
173
|
+
timezone: str = typer.Option(None, "--timezone", help="The user's time zone specified in the [IANA timezone](https:"),
|
|
174
|
+
profile_url: str = typer.Option(None, "--profile-url", help="A fully qualified URL pointing to a page representing the us"),
|
|
175
|
+
external_id: str = typer.Option(None, "--external-id", help="User identifier provided by an external provisioning source."),
|
|
176
|
+
display_name: str = typer.Option(None, "--display-name", help="The name displayed for the user in Webex."),
|
|
177
|
+
nick_name: str = typer.Option(None, "--nick-name", help="A casual name of the user. For example, Bob when the user's"),
|
|
178
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
179
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
180
|
+
):
|
|
181
|
+
"""Update a user with PUT\n\nExample --json-body:\n '{"schemas":["..."],"userName":"...","userType":"user","title":"...","active":true,"roles":[{"value":"...","type":"...","display":"..."}],"preferredLanguage":"...","locale":"..."}'."""
|
|
182
|
+
api = get_api(debug=debug)
|
|
183
|
+
org_id = resolve_org_id(api.session)
|
|
184
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Users/{user_id}"
|
|
185
|
+
if json_body:
|
|
186
|
+
body = json.loads(json_body)
|
|
187
|
+
else:
|
|
188
|
+
body = {}
|
|
189
|
+
if user_name is not None:
|
|
190
|
+
body["userName"] = user_name
|
|
191
|
+
if user_type is not None:
|
|
192
|
+
body["userType"] = user_type
|
|
193
|
+
if title is not None:
|
|
194
|
+
body["title"] = title
|
|
195
|
+
if active is not None:
|
|
196
|
+
body["active"] = active
|
|
197
|
+
if preferred_language is not None:
|
|
198
|
+
body["preferredLanguage"] = preferred_language
|
|
199
|
+
if locale is not None:
|
|
200
|
+
body["locale"] = locale
|
|
201
|
+
if timezone is not None:
|
|
202
|
+
body["timezone"] = timezone
|
|
203
|
+
if profile_url is not None:
|
|
204
|
+
body["profileUrl"] = profile_url
|
|
205
|
+
if external_id is not None:
|
|
206
|
+
body["externalId"] = external_id
|
|
207
|
+
if display_name is not None:
|
|
208
|
+
body["displayName"] = display_name
|
|
209
|
+
if nick_name is not None:
|
|
210
|
+
body["nickName"] = nick_name
|
|
211
|
+
try:
|
|
212
|
+
result = api.session.rest_put(url, json=body)
|
|
213
|
+
except WebexError as e:
|
|
214
|
+
handle_rest_error(e)
|
|
215
|
+
typer.echo(f"Updated.")
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@app.command("update-users")
|
|
220
|
+
def update_users(
|
|
221
|
+
user_id: str = typer.Argument(help="userId"),
|
|
222
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
223
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
224
|
+
):
|
|
225
|
+
"""Update a user with PATCH\n\nExample --json-body:\n '{"schemas":["..."],"Operations":[{"op":"...","path":"...","value":"..."}]}'."""
|
|
226
|
+
api = get_api(debug=debug)
|
|
227
|
+
org_id = resolve_org_id(api.session)
|
|
228
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Users/{user_id}"
|
|
229
|
+
if json_body:
|
|
230
|
+
body = json.loads(json_body)
|
|
231
|
+
else:
|
|
232
|
+
body = {}
|
|
233
|
+
try:
|
|
234
|
+
result = api.session.rest_patch(url, json=body)
|
|
235
|
+
except WebexError as e:
|
|
236
|
+
handle_rest_error(e)
|
|
237
|
+
typer.echo(f"Updated.")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@app.command("delete")
|
|
242
|
+
def delete(
|
|
243
|
+
user_id: str = typer.Argument(help="userId"),
|
|
244
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
245
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
246
|
+
):
|
|
247
|
+
"""Delete a user."""
|
|
248
|
+
if not force:
|
|
249
|
+
typer.confirm(f"Delete {user_id}?", abort=True)
|
|
250
|
+
api = get_api(debug=debug)
|
|
251
|
+
org_id = resolve_org_id(api.session)
|
|
252
|
+
url = f"https://webexapis.com/identity/scim/{org_id}/v2/Users/{user_id}"
|
|
253
|
+
try:
|
|
254
|
+
api.session.rest_delete(url)
|
|
255
|
+
except WebexError as e:
|
|
256
|
+
handle_rest_error(e)
|
|
257
|
+
typer.echo(f"Deleted: {user_id}")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@app.command("show-me")
|
|
262
|
+
def show_me(
|
|
263
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
264
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
265
|
+
):
|
|
266
|
+
"""Get Me."""
|
|
267
|
+
api = get_api(debug=debug)
|
|
268
|
+
url = f"https://webexapis.com/identity/scim/v2/Users/me"
|
|
269
|
+
try:
|
|
270
|
+
result = api.session.rest_get(url)
|
|
271
|
+
except WebexError as e:
|
|
272
|
+
handle_rest_error(e)
|
|
273
|
+
if output == "json":
|
|
274
|
+
print_json(result)
|
|
275
|
+
else:
|
|
276
|
+
if isinstance(result, dict):
|
|
277
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
278
|
+
elif isinstance(result, list):
|
|
279
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
280
|
+
else:
|
|
281
|
+
print_json(result)
|
|
282
|
+
|
|
283
|
+
|