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.
Files changed (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1610 @@
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 call-routing.")
10
+
11
+
12
+ @app.command("test-call-routing")
13
+ def test_call_routing(
14
+ originator_id: str = typer.Option(None, "--originator-id", help="This element is used to identify the originating party. It c"),
15
+ originator_type: str = typer.Option(None, "--originator-type", help="Choices: PEOPLE, TRUNK"),
16
+ originator_number: str = typer.Option(None, "--originator-number", help="Only used when `originatorType` is `TRUNK`. The `originatorN"),
17
+ destination: str = typer.Option(None, "--destination", help="This element specifies the called party. It can be any diala"),
18
+ include_applied_services: str = typer.Option(None, "--include-applied-services", help="This element is used to retrieve if any translation pattern,"),
19
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
20
+ debug: bool = typer.Option(False, "--debug"),
21
+ ):
22
+ """Test Call Routing\n\nExample --json-body:\n '{"originatorId":"...","originatorType":"PEOPLE","destination":"...","originatorNumber":"...","includeAppliedServices":true}'."""
23
+ api = get_api(debug=debug)
24
+ url = f"https://webexapis.com/v1/telephony/config/actions/testCallRouting/invoke"
25
+ params = {}
26
+ org_id = get_org_id()
27
+ if org_id is not None:
28
+ params["orgId"] = org_id
29
+ if json_body:
30
+ body = json.loads(json_body)
31
+ else:
32
+ body = {}
33
+ if originator_id is not None:
34
+ body["originatorId"] = originator_id
35
+ if originator_type is not None:
36
+ body["originatorType"] = originator_type
37
+ if originator_number is not None:
38
+ body["originatorNumber"] = originator_number
39
+ if destination is not None:
40
+ body["destination"] = destination
41
+ if include_applied_services is not None:
42
+ body["includeAppliedServices"] = include_applied_services
43
+ try:
44
+ result = api.session.rest_post(url, json=body, params=params)
45
+ except WebexError as e:
46
+ handle_rest_error(e)
47
+ print_json(result)
48
+
49
+
50
+
51
+ @app.command("list")
52
+ def cmd_list(
53
+ trunk_id: str = typer.Argument(help="trunkId"),
54
+ order: str = typer.Option(None, "--order", help="Order the trunks according to the designated fields. Availa"),
55
+ name: str = typer.Option(None, "--name", help="Return the list of trunks matching the local gateway names"),
56
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
57
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
58
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
59
+ debug: bool = typer.Option(False, "--debug"),
60
+ ):
61
+ """Get Local Gateway Dial Plan Usage for a Trunk."""
62
+ api = get_api(debug=debug)
63
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}/usageDialPlan"
64
+ params = {}
65
+ if order is not None:
66
+ params["order"] = order
67
+ if name is not None:
68
+ params["name"] = name
69
+ if limit > 0:
70
+ params["max"] = limit
71
+ if offset > 0:
72
+ params["start"] = offset
73
+ org_id = get_org_id()
74
+ if org_id is not None:
75
+ params["orgId"] = org_id
76
+ result = None
77
+ try:
78
+ result = api.session.rest_get(url, params=params)
79
+ except WebexError as e:
80
+ handle_rest_error(e)
81
+ result = result or []
82
+ items = result.get("dialPlans", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
83
+ if output == "json":
84
+ print_json(items)
85
+ else:
86
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
87
+
88
+
89
+
90
+ @app.command("list-usage-pstn-connection-trunks")
91
+ def list_usage_pstn_connection_trunks(
92
+ trunk_id: str = typer.Argument(help="trunkId"),
93
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
94
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
95
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
96
+ debug: bool = typer.Option(False, "--debug"),
97
+ ):
98
+ """Get Locations Using the Local Gateway as PSTN Connection Routing."""
99
+ api = get_api(debug=debug)
100
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}/usagePstnConnection"
101
+ params = {}
102
+ if limit > 0:
103
+ params["max"] = limit
104
+ if offset > 0:
105
+ params["start"] = offset
106
+ org_id = get_org_id()
107
+ if org_id is not None:
108
+ params["orgId"] = org_id
109
+ result = None
110
+ try:
111
+ result = api.session.rest_get(url, params=params)
112
+ except WebexError as e:
113
+ handle_rest_error(e)
114
+ result = result or []
115
+ items = result.get("locations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
116
+ if output == "json":
117
+ print_json(items)
118
+ else:
119
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
120
+
121
+
122
+
123
+ @app.command("list-usage-route-group")
124
+ def list_usage_route_group(
125
+ trunk_id: str = typer.Argument(help="trunkId"),
126
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
127
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
128
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
129
+ debug: bool = typer.Option(False, "--debug"),
130
+ ):
131
+ """Get Route Groups Using the Local Gateway."""
132
+ api = get_api(debug=debug)
133
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}/usageRouteGroup"
134
+ params = {}
135
+ if limit > 0:
136
+ params["max"] = limit
137
+ if offset > 0:
138
+ params["start"] = offset
139
+ org_id = get_org_id()
140
+ if org_id is not None:
141
+ params["orgId"] = org_id
142
+ result = None
143
+ try:
144
+ result = api.session.rest_get(url, params=params)
145
+ except WebexError as e:
146
+ handle_rest_error(e)
147
+ result = result or []
148
+ items = result.get("routeGroup", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
149
+ if output == "json":
150
+ print_json(items)
151
+ else:
152
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
153
+
154
+
155
+
156
+ @app.command("show")
157
+ def show(
158
+ trunk_id: str = typer.Argument(help="trunkId"),
159
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
160
+ debug: bool = typer.Option(False, "--debug"),
161
+ ):
162
+ """Get Local Gateway Usage Count."""
163
+ api = get_api(debug=debug)
164
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}/usage"
165
+ params = {}
166
+ org_id = get_org_id()
167
+ if org_id is not None:
168
+ params["orgId"] = org_id
169
+ try:
170
+ result = api.session.rest_get(url, params=params)
171
+ except WebexError as e:
172
+ handle_rest_error(e)
173
+ if output == "json":
174
+ print_json(result)
175
+ else:
176
+ if isinstance(result, dict):
177
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
178
+ elif isinstance(result, list):
179
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
180
+ else:
181
+ print_json(result)
182
+
183
+
184
+
185
+ @app.command("update")
186
+ def update(
187
+ dial_plan_id: str = typer.Argument(help="dialPlanId"),
188
+ delete_all_dial_patterns: bool = typer.Option(None, "--delete-all-dial-patterns/--no-delete-all-dial-patterns", help="Delete all the dial patterns for a dial plan."),
189
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
190
+ debug: bool = typer.Option(False, "--debug"),
191
+ ):
192
+ """Modify Dial Patterns\n\nExample --json-body:\n '{"dialPatterns":[{"dialPattern":"...","action":"..."}],"deleteAllDialPatterns":true}'."""
193
+ api = get_api(debug=debug)
194
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/dialPlans/{dial_plan_id}/dialPatterns"
195
+ params = {}
196
+ org_id = get_org_id()
197
+ if org_id is not None:
198
+ params["orgId"] = org_id
199
+ if json_body:
200
+ body = json.loads(json_body)
201
+ else:
202
+ body = {}
203
+ if delete_all_dial_patterns is not None:
204
+ body["deleteAllDialPatterns"] = delete_all_dial_patterns
205
+ try:
206
+ result = api.session.rest_put(url, json=body, params=params)
207
+ except WebexError as e:
208
+ handle_rest_error(e)
209
+ typer.echo(f"Updated.")
210
+
211
+
212
+
213
+ @app.command("validate-a-dial")
214
+ def validate_a_dial(
215
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
216
+ debug: bool = typer.Option(False, "--debug"),
217
+ ):
218
+ """Validate a Dial Pattern\n\nExample --json-body:\n '{"dialPatterns":["..."]}'."""
219
+ api = get_api(debug=debug)
220
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/actions/validateDialPatterns/invoke"
221
+ params = {}
222
+ org_id = get_org_id()
223
+ if org_id is not None:
224
+ params["orgId"] = org_id
225
+ if json_body:
226
+ body = json.loads(json_body)
227
+ else:
228
+ body = {}
229
+ try:
230
+ result = api.session.rest_post(url, json=body, params=params)
231
+ except WebexError as e:
232
+ handle_rest_error(e)
233
+ print_json(result)
234
+
235
+
236
+
237
+ @app.command("list-dial-plans")
238
+ def list_dial_plans(
239
+ dial_plan_name: str = typer.Option(None, "--dial-plan-name", help="Return the list of dial plans matching the dial plan name."),
240
+ route_group_name: str = typer.Option(None, "--route-group-name", help="Return the list of dial plans matching the Route group name."),
241
+ trunk_name: str = typer.Option(None, "--trunk-name", help="Return the list of dial plans matching the Trunk name.."),
242
+ order: str = typer.Option(None, "--order", help="Order the dial plans according to the designated fields. Av"),
243
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
244
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
245
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
246
+ debug: bool = typer.Option(False, "--debug"),
247
+ ):
248
+ """Read the List of Dial Plans."""
249
+ api = get_api(debug=debug)
250
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/dialPlans"
251
+ params = {}
252
+ if dial_plan_name is not None:
253
+ params["dialPlanName"] = dial_plan_name
254
+ if route_group_name is not None:
255
+ params["routeGroupName"] = route_group_name
256
+ if trunk_name is not None:
257
+ params["trunkName"] = trunk_name
258
+ if order is not None:
259
+ params["order"] = order
260
+ if limit > 0:
261
+ params["max"] = limit
262
+ if offset > 0:
263
+ params["start"] = offset
264
+ org_id = get_org_id()
265
+ if org_id is not None:
266
+ params["orgId"] = org_id
267
+ result = None
268
+ try:
269
+ result = api.session.rest_get(url, params=params)
270
+ except WebexError as e:
271
+ handle_rest_error(e)
272
+ result = result or []
273
+ items = result.get("dialPlans", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
274
+ if output == "json":
275
+ print_json(items)
276
+ else:
277
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
278
+
279
+
280
+
281
+ @app.command("create")
282
+ def create(
283
+ name: str = typer.Option(None, "--name", help="(required) A unique name for the dial plan."),
284
+ route_id: str = typer.Option(None, "--route-id", help="(required) ID of route type associated with the dial plan."),
285
+ route_type: str = typer.Option(None, "--route-type", help="(required) Choices: ROUTE_GROUP, TRUNK"),
286
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
287
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
288
+ debug: bool = typer.Option(False, "--debug"),
289
+ ):
290
+ """Create a Dial Plan\n\nExample --json-body:\n '{"name":"...","routeId":"...","routeType":"ROUTE_GROUP","dialPatterns":["..."]}'."""
291
+ api = get_api(debug=debug)
292
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/dialPlans"
293
+ params = {}
294
+ org_id = get_org_id()
295
+ if org_id is not None:
296
+ params["orgId"] = org_id
297
+ if json_body:
298
+ body = json.loads(json_body)
299
+ else:
300
+ body = {}
301
+ if name is not None:
302
+ body["name"] = name
303
+ if route_id is not None:
304
+ body["routeId"] = route_id
305
+ if route_type is not None:
306
+ body["routeType"] = route_type
307
+ _missing = [f for f in ['name', 'routeId', 'routeType'] if f not in body or body[f] is None]
308
+ if _missing:
309
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
310
+ raise typer.Exit(1)
311
+ try:
312
+ result = api.session.rest_post(url, json=body, params=params)
313
+ except WebexError as e:
314
+ handle_rest_error(e)
315
+ if output == "json":
316
+ print_json(result)
317
+ elif isinstance(result, dict) and "id" in result:
318
+ typer.echo(f"Created: {result['id']}")
319
+ elif not result or result == {}:
320
+ typer.echo("Created.")
321
+ else:
322
+ print_json(result)
323
+
324
+
325
+
326
+ @app.command("show-dial-plans")
327
+ def show_dial_plans(
328
+ dial_plan_id: str = typer.Argument(help="dialPlanId"),
329
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
330
+ debug: bool = typer.Option(False, "--debug"),
331
+ ):
332
+ """Get a Dial Plan."""
333
+ api = get_api(debug=debug)
334
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/dialPlans/{dial_plan_id}"
335
+ params = {}
336
+ org_id = get_org_id()
337
+ if org_id is not None:
338
+ params["orgId"] = org_id
339
+ try:
340
+ result = api.session.rest_get(url, params=params)
341
+ except WebexError as e:
342
+ handle_rest_error(e)
343
+ if output == "json":
344
+ print_json(result)
345
+ else:
346
+ if isinstance(result, dict):
347
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
348
+ elif isinstance(result, list):
349
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
350
+ else:
351
+ print_json(result)
352
+
353
+
354
+
355
+ @app.command("update-dial-plans")
356
+ def update_dial_plans(
357
+ dial_plan_id: str = typer.Argument(help="dialPlanId"),
358
+ name: str = typer.Option(None, "--name", help="A unique name for the dial plan."),
359
+ route_id: str = typer.Option(None, "--route-id", help="ID of route type associated with the dial plan."),
360
+ route_type: str = typer.Option(None, "--route-type", help="Choices: ROUTE_GROUP, TRUNK"),
361
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
362
+ debug: bool = typer.Option(False, "--debug"),
363
+ ):
364
+ """Modify a Dial Plan\n\nExample --json-body:\n '{"name":"...","routeId":"...","routeType":"ROUTE_GROUP"}'."""
365
+ api = get_api(debug=debug)
366
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/dialPlans/{dial_plan_id}"
367
+ params = {}
368
+ org_id = get_org_id()
369
+ if org_id is not None:
370
+ params["orgId"] = org_id
371
+ if json_body:
372
+ body = json.loads(json_body)
373
+ else:
374
+ body = {}
375
+ if name is not None:
376
+ body["name"] = name
377
+ if route_id is not None:
378
+ body["routeId"] = route_id
379
+ if route_type is not None:
380
+ body["routeType"] = route_type
381
+ try:
382
+ result = api.session.rest_put(url, json=body, params=params)
383
+ except WebexError as e:
384
+ handle_rest_error(e)
385
+ typer.echo(f"Updated.")
386
+
387
+
388
+
389
+ @app.command("delete")
390
+ def delete(
391
+ dial_plan_id: str = typer.Argument(help="dialPlanId"),
392
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
393
+ debug: bool = typer.Option(False, "--debug"),
394
+ ):
395
+ """Delete a Dial Plan."""
396
+ if not force:
397
+ typer.confirm(f"Delete {dial_plan_id}?", abort=True)
398
+ api = get_api(debug=debug)
399
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/dialPlans/{dial_plan_id}"
400
+ params = {}
401
+ org_id = get_org_id()
402
+ if org_id is not None:
403
+ params["orgId"] = org_id
404
+ try:
405
+ api.session.rest_delete(url, params=params)
406
+ except WebexError as e:
407
+ handle_rest_error(e)
408
+ typer.echo(f"Deleted: {dial_plan_id}")
409
+
410
+
411
+
412
+ @app.command("validate-local-gateway")
413
+ def validate_local_gateway(
414
+ address: str = typer.Option(None, "--address", help="FQDN or SRV address of the trunk."),
415
+ domain: str = typer.Option(None, "--domain", help="Domain name of the trunk."),
416
+ port: str = typer.Option(None, "--port", help="FQDN port of the trunk."),
417
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
418
+ debug: bool = typer.Option(False, "--debug"),
419
+ ):
420
+ """Validate Local Gateway FQDN and Domain for a Trunk."""
421
+ api = get_api(debug=debug)
422
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/actions/fqdnValidation/invoke"
423
+ params = {}
424
+ org_id = get_org_id()
425
+ if org_id is not None:
426
+ params["orgId"] = org_id
427
+ if json_body:
428
+ body = json.loads(json_body)
429
+ else:
430
+ body = {}
431
+ if address is not None:
432
+ body["address"] = address
433
+ if domain is not None:
434
+ body["domain"] = domain
435
+ if port is not None:
436
+ body["port"] = port
437
+ try:
438
+ result = api.session.rest_post(url, json=body, params=params)
439
+ except WebexError as e:
440
+ handle_rest_error(e)
441
+ print_json(result)
442
+
443
+
444
+
445
+ @app.command("list-trunks")
446
+ def list_trunks(
447
+ name: str = typer.Option(None, "--name", help="Return the list of trunks matching the local gateway names."),
448
+ location_name: str = typer.Option(None, "--location-name", help="Return the list of trunks matching the location names."),
449
+ trunk_type: str = typer.Option(None, "--trunk-type", help="Return the list of trunks matching the trunk type."),
450
+ order: str = typer.Option(None, "--order", help="Order the trunks according to the designated fields. Availa"),
451
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
452
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
453
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
454
+ debug: bool = typer.Option(False, "--debug"),
455
+ ):
456
+ """Read the List of Trunks."""
457
+ api = get_api(debug=debug)
458
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks"
459
+ params = {}
460
+ if name is not None:
461
+ params["name"] = name
462
+ if location_name is not None:
463
+ params["locationName"] = location_name
464
+ if trunk_type is not None:
465
+ params["trunkType"] = trunk_type
466
+ if order is not None:
467
+ params["order"] = order
468
+ if limit > 0:
469
+ params["max"] = limit
470
+ if offset > 0:
471
+ params["start"] = offset
472
+ org_id = get_org_id()
473
+ if org_id is not None:
474
+ params["orgId"] = org_id
475
+ result = None
476
+ try:
477
+ result = api.session.rest_get(url, params=params)
478
+ except WebexError as e:
479
+ handle_rest_error(e)
480
+ result = result or []
481
+ items = result.get("trunks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
482
+ if output == "json":
483
+ print_json(items)
484
+ else:
485
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
486
+
487
+
488
+
489
+ @app.command("create-trunks")
490
+ def create_trunks(
491
+ name: str = typer.Option(None, "--name", help="(required) A unique name for the trunk."),
492
+ location_id: str = typer.Option(None, "--location-id", help="(required) ID of location associated with the trunk."),
493
+ password: str = typer.Option(None, "--password", help="(required) A password to use on the trunk."),
494
+ dual_identity_support_enabled: bool = typer.Option(None, "--dual-identity-support-enabled/--no-dual-identity-support-enabled", help="Dual Identity Support setting impacts the handling of the Fr"),
495
+ trunk_type: str = typer.Option(None, "--trunk-type", help="(required) Choices: REGISTERING, CERTIFICATE_BASED"),
496
+ device_type: str = typer.Option(None, "--device-type", help="Device type assosiated with trunk."),
497
+ address: str = typer.Option(None, "--address", help="FQDN or SRV address. Required to create a static certificate"),
498
+ domain: str = typer.Option(None, "--domain", help="Domain name. Required to create a static certificate based t"),
499
+ port: str = typer.Option(None, "--port", help="FQDN port. Required to create a static certificate-based tru"),
500
+ max_concurrent_calls: str = typer.Option(None, "--max-concurrent-calls", help="Max Concurrent call. Required to create a static certificate"),
501
+ p_charge_info_support_policy: str = typer.Option(None, "--p-charge-info-support-policy", help="Choices: DISABLED, ASSERTED_IDENTITY, CONFIGURABLE_CHARGE_NUMBER"),
502
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
503
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
504
+ debug: bool = typer.Option(False, "--debug"),
505
+ ):
506
+ """Create a Trunk\n\nExample --json-body:\n '{"name":"...","locationId":"...","password":"...","trunkType":"REGISTERING","dualIdentitySupportEnabled":true,"deviceType":"...","address":"...","domain":"..."}'."""
507
+ api = get_api(debug=debug)
508
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks"
509
+ params = {}
510
+ org_id = get_org_id()
511
+ if org_id is not None:
512
+ params["orgId"] = org_id
513
+ if json_body:
514
+ body = json.loads(json_body)
515
+ else:
516
+ body = {}
517
+ if name is not None:
518
+ body["name"] = name
519
+ if location_id is not None:
520
+ body["locationId"] = location_id
521
+ if password is not None:
522
+ body["password"] = password
523
+ if dual_identity_support_enabled is not None:
524
+ body["dualIdentitySupportEnabled"] = dual_identity_support_enabled
525
+ if trunk_type is not None:
526
+ body["trunkType"] = trunk_type
527
+ if device_type is not None:
528
+ body["deviceType"] = device_type
529
+ if address is not None:
530
+ body["address"] = address
531
+ if domain is not None:
532
+ body["domain"] = domain
533
+ if port is not None:
534
+ body["port"] = port
535
+ if max_concurrent_calls is not None:
536
+ body["maxConcurrentCalls"] = max_concurrent_calls
537
+ if p_charge_info_support_policy is not None:
538
+ body["pChargeInfoSupportPolicy"] = p_charge_info_support_policy
539
+ _missing = [f for f in ['name', 'locationId', 'password', 'trunkType'] if f not in body or body[f] is None]
540
+ if _missing:
541
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
542
+ raise typer.Exit(1)
543
+ try:
544
+ result = api.session.rest_post(url, json=body, params=params)
545
+ except WebexError as e:
546
+ handle_rest_error(e)
547
+ if output == "json":
548
+ print_json(result)
549
+ elif isinstance(result, dict) and "id" in result:
550
+ typer.echo(f"Created: {result['id']}")
551
+ elif not result or result == {}:
552
+ typer.echo("Created.")
553
+ else:
554
+ print_json(result)
555
+
556
+
557
+
558
+ @app.command("show-trunks")
559
+ def show_trunks(
560
+ trunk_id: str = typer.Argument(help="trunkId"),
561
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
562
+ debug: bool = typer.Option(False, "--debug"),
563
+ ):
564
+ """Get a Trunk."""
565
+ api = get_api(debug=debug)
566
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}"
567
+ params = {}
568
+ org_id = get_org_id()
569
+ if org_id is not None:
570
+ params["orgId"] = org_id
571
+ try:
572
+ result = api.session.rest_get(url, params=params)
573
+ except WebexError as e:
574
+ handle_rest_error(e)
575
+ if output == "json":
576
+ print_json(result)
577
+ else:
578
+ if isinstance(result, dict):
579
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
580
+ elif isinstance(result, list):
581
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
582
+ else:
583
+ print_json(result)
584
+
585
+
586
+
587
+ @app.command("update-trunks")
588
+ def update_trunks(
589
+ trunk_id: str = typer.Argument(help="trunkId"),
590
+ name: str = typer.Option(None, "--name", help="A unique name for the dial plan."),
591
+ password: str = typer.Option(None, "--password", help="A password to use on the trunk."),
592
+ dual_identity_support_enabled: bool = typer.Option(None, "--dual-identity-support-enabled/--no-dual-identity-support-enabled", help="Determines the behavior of the From and PAI headers on outbo"),
593
+ max_concurrent_calls: str = typer.Option(None, "--max-concurrent-calls", help="Max Concurrent call. Required to create a static certificate"),
594
+ p_charge_info_support_policy: str = typer.Option(None, "--p-charge-info-support-policy", help="Choices: DISABLED, ASSERTED_IDENTITY, CONFIGURABLE_CHARGE_NUMBER"),
595
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
596
+ debug: bool = typer.Option(False, "--debug"),
597
+ ):
598
+ """Modify a Trunk\n\nExample --json-body:\n '{"name":"...","password":"...","dualIdentitySupportEnabled":true,"maxConcurrentCalls":0,"pChargeInfoSupportPolicy":"DISABLED"}'."""
599
+ api = get_api(debug=debug)
600
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}"
601
+ params = {}
602
+ org_id = get_org_id()
603
+ if org_id is not None:
604
+ params["orgId"] = org_id
605
+ if json_body:
606
+ body = json.loads(json_body)
607
+ else:
608
+ body = {}
609
+ if name is not None:
610
+ body["name"] = name
611
+ if password is not None:
612
+ body["password"] = password
613
+ if dual_identity_support_enabled is not None:
614
+ body["dualIdentitySupportEnabled"] = dual_identity_support_enabled
615
+ if max_concurrent_calls is not None:
616
+ body["maxConcurrentCalls"] = max_concurrent_calls
617
+ if p_charge_info_support_policy is not None:
618
+ body["pChargeInfoSupportPolicy"] = p_charge_info_support_policy
619
+ try:
620
+ result = api.session.rest_put(url, json=body, params=params)
621
+ except WebexError as e:
622
+ handle_rest_error(e)
623
+ typer.echo(f"Updated.")
624
+
625
+
626
+
627
+ @app.command("delete-trunks")
628
+ def delete_trunks(
629
+ trunk_id: str = typer.Argument(help="trunkId"),
630
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
631
+ debug: bool = typer.Option(False, "--debug"),
632
+ ):
633
+ """Delete a Trunk."""
634
+ if not force:
635
+ typer.confirm(f"Delete {trunk_id}?", abort=True)
636
+ api = get_api(debug=debug)
637
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}"
638
+ params = {}
639
+ org_id = get_org_id()
640
+ if org_id is not None:
641
+ params["orgId"] = org_id
642
+ try:
643
+ api.session.rest_delete(url, params=params)
644
+ except WebexError as e:
645
+ handle_rest_error(e)
646
+ typer.echo(f"Deleted: {trunk_id}")
647
+
648
+
649
+
650
+ @app.command("list-trunk-types")
651
+ def list_trunk_types(
652
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
653
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
654
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
655
+ debug: bool = typer.Option(False, "--debug"),
656
+ ):
657
+ """Read the List of Trunk Types."""
658
+ api = get_api(debug=debug)
659
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/trunkTypes"
660
+ params = {}
661
+ if limit > 0:
662
+ params["max"] = limit
663
+ if offset > 0:
664
+ params["start"] = offset
665
+ org_id = get_org_id()
666
+ if org_id is not None:
667
+ params["orgId"] = org_id
668
+ result = None
669
+ try:
670
+ result = api.session.rest_get(url, params=params)
671
+ except WebexError as e:
672
+ handle_rest_error(e)
673
+ result = result or []
674
+ items = result.get("trunkTypes", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
675
+ if output == "json":
676
+ print_json(items)
677
+ else:
678
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
679
+
680
+
681
+
682
+ @app.command("list-route-groups")
683
+ def list_route_groups(
684
+ name: str = typer.Option(None, "--name", help="Return the list of route groups matching the Route group nam"),
685
+ order: str = typer.Option(None, "--order", help="Order the route groups according to designated fields. Avai"),
686
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
687
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
688
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
689
+ debug: bool = typer.Option(False, "--debug"),
690
+ ):
691
+ """Read the List of Routing Groups."""
692
+ api = get_api(debug=debug)
693
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups"
694
+ params = {}
695
+ if name is not None:
696
+ params["name"] = name
697
+ if order is not None:
698
+ params["order"] = order
699
+ if limit > 0:
700
+ params["max"] = limit
701
+ if offset > 0:
702
+ params["start"] = offset
703
+ org_id = get_org_id()
704
+ if org_id is not None:
705
+ params["orgId"] = org_id
706
+ result = None
707
+ try:
708
+ result = api.session.rest_get(url, params=params)
709
+ except WebexError as e:
710
+ handle_rest_error(e)
711
+ result = result or []
712
+ items = result.get("routeGroups", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
713
+ if output == "json":
714
+ print_json(items)
715
+ else:
716
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
717
+
718
+
719
+
720
+ @app.command("create-route-groups")
721
+ def create_route_groups(
722
+ name: str = typer.Option(None, "--name", help="(required) A unique name for the Route Group."),
723
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
724
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
725
+ debug: bool = typer.Option(False, "--debug"),
726
+ ):
727
+ """Create Route Group for a Organization\n\nExample --json-body:\n '{"name":"...","localGateways":[{"id":"...","priority":"...","name":"...","locationId":"..."}]}'."""
728
+ api = get_api(debug=debug)
729
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups"
730
+ params = {}
731
+ org_id = get_org_id()
732
+ if org_id is not None:
733
+ params["orgId"] = org_id
734
+ if json_body:
735
+ body = json.loads(json_body)
736
+ else:
737
+ body = {}
738
+ if name is not None:
739
+ body["name"] = name
740
+ _missing = [f for f in ['name'] if f not in body or body[f] is None]
741
+ if _missing:
742
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
743
+ raise typer.Exit(1)
744
+ try:
745
+ result = api.session.rest_post(url, json=body, params=params)
746
+ except WebexError as e:
747
+ handle_rest_error(e)
748
+ if output == "json":
749
+ print_json(result)
750
+ elif isinstance(result, dict) and "id" in result:
751
+ typer.echo(f"Created: {result['id']}")
752
+ elif not result or result == {}:
753
+ typer.echo("Created.")
754
+ else:
755
+ print_json(result)
756
+
757
+
758
+
759
+ @app.command("show-route-groups")
760
+ def show_route_groups(
761
+ route_group_id: str = typer.Argument(help="routeGroupId"),
762
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
763
+ debug: bool = typer.Option(False, "--debug"),
764
+ ):
765
+ """Read a Route Group for a Organization."""
766
+ api = get_api(debug=debug)
767
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}"
768
+ params = {}
769
+ org_id = get_org_id()
770
+ if org_id is not None:
771
+ params["orgId"] = org_id
772
+ try:
773
+ result = api.session.rest_get(url, params=params)
774
+ except WebexError as e:
775
+ handle_rest_error(e)
776
+ if output == "json":
777
+ print_json(result)
778
+ else:
779
+ if isinstance(result, dict):
780
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
781
+ elif isinstance(result, list):
782
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
783
+ else:
784
+ print_json(result)
785
+
786
+
787
+
788
+ @app.command("update-route-groups")
789
+ def update_route_groups(
790
+ route_group_id: str = typer.Argument(help="routeGroupId"),
791
+ name: str = typer.Option(None, "--name", help="A unique name for the Route Group."),
792
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
793
+ debug: bool = typer.Option(False, "--debug"),
794
+ ):
795
+ """Modify a Route Group for a Organization\n\nExample --json-body:\n '{"name":"...","localGateways":[{"id":"...","priority":"...","name":"...","locationId":"..."}]}'."""
796
+ api = get_api(debug=debug)
797
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}"
798
+ params = {}
799
+ org_id = get_org_id()
800
+ if org_id is not None:
801
+ params["orgId"] = org_id
802
+ if json_body:
803
+ body = json.loads(json_body)
804
+ else:
805
+ body = {}
806
+ if name is not None:
807
+ body["name"] = name
808
+ try:
809
+ result = api.session.rest_put(url, json=body, params=params)
810
+ except WebexError as e:
811
+ handle_rest_error(e)
812
+ typer.echo(f"Updated.")
813
+
814
+
815
+
816
+ @app.command("delete-route-groups")
817
+ def delete_route_groups(
818
+ route_group_id: str = typer.Argument(help="routeGroupId"),
819
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
820
+ debug: bool = typer.Option(False, "--debug"),
821
+ ):
822
+ """Remove a Route Group from an Organization."""
823
+ if not force:
824
+ typer.confirm(f"Delete {route_group_id}?", abort=True)
825
+ api = get_api(debug=debug)
826
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}"
827
+ params = {}
828
+ org_id = get_org_id()
829
+ if org_id is not None:
830
+ params["orgId"] = org_id
831
+ try:
832
+ api.session.rest_delete(url, params=params)
833
+ except WebexError as e:
834
+ handle_rest_error(e)
835
+ typer.echo(f"Deleted: {route_group_id}")
836
+
837
+
838
+
839
+ @app.command("show-usage")
840
+ def show_usage(
841
+ route_group_id: str = typer.Argument(help="routeGroupId"),
842
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
843
+ debug: bool = typer.Option(False, "--debug"),
844
+ ):
845
+ """Read the Usage of a Routing Group."""
846
+ api = get_api(debug=debug)
847
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}/usage"
848
+ params = {}
849
+ org_id = get_org_id()
850
+ if org_id is not None:
851
+ params["orgId"] = org_id
852
+ try:
853
+ result = api.session.rest_get(url, params=params)
854
+ except WebexError as e:
855
+ handle_rest_error(e)
856
+ if output == "json":
857
+ print_json(result)
858
+ else:
859
+ if isinstance(result, dict):
860
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
861
+ elif isinstance(result, list):
862
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
863
+ else:
864
+ print_json(result)
865
+
866
+
867
+
868
+ @app.command("list-usage-call-to-extension-route-groups")
869
+ def list_usage_call_to_extension_route_groups(
870
+ route_group_id: str = typer.Argument(help="routeGroupId"),
871
+ location_name: str = typer.Option(None, "--location-name", help="Return the list of locations matching the location name."),
872
+ order: str = typer.Option(None, "--order", help="Order the locations according to designated fields. Availab"),
873
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
874
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
875
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
876
+ debug: bool = typer.Option(False, "--debug"),
877
+ ):
878
+ """Read the Call to Extension Locations of a Routing Group."""
879
+ api = get_api(debug=debug)
880
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}/usageCallToExtension"
881
+ params = {}
882
+ if location_name is not None:
883
+ params["locationName"] = location_name
884
+ if order is not None:
885
+ params["order"] = order
886
+ if limit > 0:
887
+ params["max"] = limit
888
+ if offset > 0:
889
+ params["start"] = offset
890
+ org_id = get_org_id()
891
+ if org_id is not None:
892
+ params["orgId"] = org_id
893
+ result = None
894
+ try:
895
+ result = api.session.rest_get(url, params=params)
896
+ except WebexError as e:
897
+ handle_rest_error(e)
898
+ result = result or []
899
+ items = result.get("locations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
900
+ if output == "json":
901
+ print_json(items)
902
+ else:
903
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
904
+
905
+
906
+
907
+ @app.command("list-usage-dial-plan")
908
+ def list_usage_dial_plan(
909
+ route_group_id: str = typer.Argument(help="routeGroupId"),
910
+ location_name: str = typer.Option(None, "--location-name", help="Return the list of locations matching the location name."),
911
+ order: str = typer.Option(None, "--order", help="Order the locations according to designated fields. Availab"),
912
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
913
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
914
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
915
+ debug: bool = typer.Option(False, "--debug"),
916
+ ):
917
+ """Read the Dial Plan Locations of a Routing Group."""
918
+ api = get_api(debug=debug)
919
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}/usageDialPlan"
920
+ params = {}
921
+ if location_name is not None:
922
+ params["locationName"] = location_name
923
+ if order is not None:
924
+ params["order"] = order
925
+ if limit > 0:
926
+ params["max"] = limit
927
+ if offset > 0:
928
+ params["start"] = offset
929
+ org_id = get_org_id()
930
+ if org_id is not None:
931
+ params["orgId"] = org_id
932
+ result = None
933
+ try:
934
+ result = api.session.rest_get(url, params=params)
935
+ except WebexError as e:
936
+ handle_rest_error(e)
937
+ result = result or []
938
+ items = result.get("locations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
939
+ if output == "json":
940
+ print_json(items)
941
+ else:
942
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
943
+
944
+
945
+
946
+ @app.command("list-usage-pstn-connection-route-groups")
947
+ def list_usage_pstn_connection_route_groups(
948
+ route_group_id: str = typer.Argument(help="routeGroupId"),
949
+ location_name: str = typer.Option(None, "--location-name", help="Return the list of locations matching the location name."),
950
+ order: str = typer.Option(None, "--order", help="Order the locations according to designated fields. Availab"),
951
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
952
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
953
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
954
+ debug: bool = typer.Option(False, "--debug"),
955
+ ):
956
+ """Read the PSTN Connection Locations of a Routing Group."""
957
+ api = get_api(debug=debug)
958
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}/usagePstnConnection"
959
+ params = {}
960
+ if location_name is not None:
961
+ params["locationName"] = location_name
962
+ if order is not None:
963
+ params["order"] = order
964
+ if limit > 0:
965
+ params["max"] = limit
966
+ if offset > 0:
967
+ params["start"] = offset
968
+ org_id = get_org_id()
969
+ if org_id is not None:
970
+ params["orgId"] = org_id
971
+ result = None
972
+ try:
973
+ result = api.session.rest_get(url, params=params)
974
+ except WebexError as e:
975
+ handle_rest_error(e)
976
+ result = result or []
977
+ items = result.get("locations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
978
+ if output == "json":
979
+ print_json(items)
980
+ else:
981
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
982
+
983
+
984
+
985
+ @app.command("list-usage-route-list")
986
+ def list_usage_route_list(
987
+ route_group_id: str = typer.Argument(help="routeGroupId"),
988
+ name: str = typer.Option(None, "--name", help="Return the list of locations matching the location name."),
989
+ order: str = typer.Option(None, "--order", help="Order the locations according to designated fields. Availab"),
990
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
991
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
992
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
993
+ debug: bool = typer.Option(False, "--debug"),
994
+ ):
995
+ """Read the Route Lists of a Routing Group."""
996
+ api = get_api(debug=debug)
997
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeGroups/{route_group_id}/usageRouteList"
998
+ params = {}
999
+ if name is not None:
1000
+ params["name"] = name
1001
+ if order is not None:
1002
+ params["order"] = order
1003
+ if limit > 0:
1004
+ params["max"] = limit
1005
+ if offset > 0:
1006
+ params["start"] = offset
1007
+ org_id = get_org_id()
1008
+ if org_id is not None:
1009
+ params["orgId"] = org_id
1010
+ result = None
1011
+ try:
1012
+ result = api.session.rest_get(url, params=params)
1013
+ except WebexError as e:
1014
+ handle_rest_error(e)
1015
+ result = result or []
1016
+ items = result.get("routeGroupUsageRouteListGet", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1017
+ if output == "json":
1018
+ print_json(items)
1019
+ else:
1020
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
1021
+
1022
+
1023
+
1024
+ @app.command("list-route-lists")
1025
+ def list_route_lists(
1026
+ order: str = typer.Option(None, "--order", help="Order the Route List according to the designated fields. Ava"),
1027
+ name: str = typer.Option(None, "--name", help="Return the list of Route List matching the route list name."),
1028
+ location_id: str = typer.Option(None, "--location-id", help="Return the list of Route Lists matching the location id."),
1029
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1030
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1031
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1032
+ debug: bool = typer.Option(False, "--debug"),
1033
+ ):
1034
+ """Read the List of Route Lists."""
1035
+ api = get_api(debug=debug)
1036
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeLists"
1037
+ params = {}
1038
+ if order is not None:
1039
+ params["order"] = order
1040
+ if name is not None:
1041
+ params["name"] = name
1042
+ if location_id is not None:
1043
+ params["locationId"] = location_id
1044
+ if limit > 0:
1045
+ params["max"] = limit
1046
+ if offset > 0:
1047
+ params["start"] = offset
1048
+ org_id = get_org_id()
1049
+ if org_id is not None:
1050
+ params["orgId"] = org_id
1051
+ result = None
1052
+ try:
1053
+ result = api.session.rest_get(url, params=params)
1054
+ except WebexError as e:
1055
+ handle_rest_error(e)
1056
+ result = result or []
1057
+ items = result.get("routeLists", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1058
+ if output == "json":
1059
+ print_json(items)
1060
+ else:
1061
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
1062
+
1063
+
1064
+
1065
+ @app.command("create-route-lists")
1066
+ def create_route_lists(
1067
+ name: str = typer.Option(None, "--name", help="(required) Name of the Route List"),
1068
+ location_id: str = typer.Option(None, "--location-id", help="(required) Location associated with the Route List."),
1069
+ route_group_id: str = typer.Option(None, "--route-group-id", help="(required) ID of the route group associated with Route List."),
1070
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1071
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1072
+ debug: bool = typer.Option(False, "--debug"),
1073
+ ):
1074
+ """Create a Route List."""
1075
+ api = get_api(debug=debug)
1076
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeLists"
1077
+ params = {}
1078
+ org_id = get_org_id()
1079
+ if org_id is not None:
1080
+ params["orgId"] = org_id
1081
+ if json_body:
1082
+ body = json.loads(json_body)
1083
+ else:
1084
+ body = {}
1085
+ if name is not None:
1086
+ body["name"] = name
1087
+ if location_id is not None:
1088
+ body["locationId"] = location_id
1089
+ if route_group_id is not None:
1090
+ body["routeGroupId"] = route_group_id
1091
+ _missing = [f for f in ['name', 'locationId', 'routeGroupId'] if f not in body or body[f] is None]
1092
+ if _missing:
1093
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1094
+ raise typer.Exit(1)
1095
+ try:
1096
+ result = api.session.rest_post(url, json=body, params=params)
1097
+ except WebexError as e:
1098
+ handle_rest_error(e)
1099
+ if output == "json":
1100
+ print_json(result)
1101
+ elif isinstance(result, dict) and "id" in result:
1102
+ typer.echo(f"Created: {result['id']}")
1103
+ elif not result or result == {}:
1104
+ typer.echo("Created.")
1105
+ else:
1106
+ print_json(result)
1107
+
1108
+
1109
+
1110
+ @app.command("show-route-lists")
1111
+ def show_route_lists(
1112
+ route_list_id: str = typer.Argument(help="routeListId"),
1113
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1114
+ debug: bool = typer.Option(False, "--debug"),
1115
+ ):
1116
+ """Get a Route List."""
1117
+ api = get_api(debug=debug)
1118
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeLists/{route_list_id}"
1119
+ params = {}
1120
+ org_id = get_org_id()
1121
+ if org_id is not None:
1122
+ params["orgId"] = org_id
1123
+ try:
1124
+ result = api.session.rest_get(url, params=params)
1125
+ except WebexError as e:
1126
+ handle_rest_error(e)
1127
+ if output == "json":
1128
+ print_json(result)
1129
+ else:
1130
+ if isinstance(result, dict):
1131
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1132
+ elif isinstance(result, list):
1133
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1134
+ else:
1135
+ print_json(result)
1136
+
1137
+
1138
+
1139
+ @app.command("update-route-lists")
1140
+ def update_route_lists(
1141
+ route_list_id: str = typer.Argument(help="routeListId"),
1142
+ name: str = typer.Option(None, "--name", help="Route List new name."),
1143
+ route_group_id: str = typer.Option(None, "--route-group-id", help="New route group ID."),
1144
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1145
+ debug: bool = typer.Option(False, "--debug"),
1146
+ ):
1147
+ """Modify a Route List."""
1148
+ api = get_api(debug=debug)
1149
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeLists/{route_list_id}"
1150
+ params = {}
1151
+ org_id = get_org_id()
1152
+ if org_id is not None:
1153
+ params["orgId"] = org_id
1154
+ if json_body:
1155
+ body = json.loads(json_body)
1156
+ else:
1157
+ body = {}
1158
+ if name is not None:
1159
+ body["name"] = name
1160
+ if route_group_id is not None:
1161
+ body["routeGroupId"] = route_group_id
1162
+ try:
1163
+ result = api.session.rest_put(url, json=body, params=params)
1164
+ except WebexError as e:
1165
+ handle_rest_error(e)
1166
+ typer.echo(f"Updated.")
1167
+
1168
+
1169
+
1170
+ @app.command("delete-route-lists")
1171
+ def delete_route_lists(
1172
+ route_list_id: str = typer.Argument(help="routeListId"),
1173
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1174
+ debug: bool = typer.Option(False, "--debug"),
1175
+ ):
1176
+ """Delete a Route List."""
1177
+ if not force:
1178
+ typer.confirm(f"Delete {route_list_id}?", abort=True)
1179
+ api = get_api(debug=debug)
1180
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeLists/{route_list_id}"
1181
+ params = {}
1182
+ org_id = get_org_id()
1183
+ if org_id is not None:
1184
+ params["orgId"] = org_id
1185
+ try:
1186
+ api.session.rest_delete(url, params=params)
1187
+ except WebexError as e:
1188
+ handle_rest_error(e)
1189
+ typer.echo(f"Deleted: {route_list_id}")
1190
+
1191
+
1192
+
1193
+ @app.command("list-numbers")
1194
+ def list_numbers(
1195
+ route_list_id: str = typer.Argument(help="routeListId"),
1196
+ number: str = typer.Option(None, "--number", help="Number assigned to the route list."),
1197
+ order: str = typer.Option(None, "--order", help="Order the Route Lists according to number, ascending or desc"),
1198
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1199
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1200
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1201
+ debug: bool = typer.Option(False, "--debug"),
1202
+ ):
1203
+ """Get Numbers assigned to a Route List."""
1204
+ api = get_api(debug=debug)
1205
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeLists/{route_list_id}/numbers"
1206
+ params = {}
1207
+ if number is not None:
1208
+ params["number"] = number
1209
+ if order is not None:
1210
+ params["order"] = order
1211
+ if limit > 0:
1212
+ params["max"] = limit
1213
+ if offset > 0:
1214
+ params["start"] = offset
1215
+ org_id = get_org_id()
1216
+ if org_id is not None:
1217
+ params["orgId"] = org_id
1218
+ result = None
1219
+ try:
1220
+ result = api.session.rest_get(url, params=params)
1221
+ except WebexError as e:
1222
+ handle_rest_error(e)
1223
+ result = result or []
1224
+ items = result.get("numbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1225
+ if output == "json":
1226
+ print_json(items)
1227
+ else:
1228
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
1229
+
1230
+
1231
+
1232
+ @app.command("update-numbers")
1233
+ def update_numbers(
1234
+ route_list_id: str = typer.Argument(help="routeListId"),
1235
+ delete_all_numbers: bool = typer.Option(None, "--delete-all-numbers/--no-delete-all-numbers", help="If present, the numbers array is ignored and all numbers in"),
1236
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1237
+ debug: bool = typer.Option(False, "--debug"),
1238
+ ):
1239
+ """Modify Numbers for Route List\n\nExample --json-body:\n '{"numbers":[{"number":"...","action":"..."}],"deleteAllNumbers":true}'."""
1240
+ api = get_api(debug=debug)
1241
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/routeLists/{route_list_id}/numbers"
1242
+ params = {}
1243
+ org_id = get_org_id()
1244
+ if org_id is not None:
1245
+ params["orgId"] = org_id
1246
+ if json_body:
1247
+ body = json.loads(json_body)
1248
+ else:
1249
+ body = {}
1250
+ if delete_all_numbers is not None:
1251
+ body["deleteAllNumbers"] = delete_all_numbers
1252
+ try:
1253
+ result = api.session.rest_put(url, json=body, params=params)
1254
+ except WebexError as e:
1255
+ handle_rest_error(e)
1256
+ typer.echo(f"Updated.")
1257
+
1258
+
1259
+
1260
+ @app.command("list-usage-call-to-extension-trunks")
1261
+ def list_usage_call_to_extension_trunks(
1262
+ trunk_id: str = typer.Argument(help="trunkId"),
1263
+ order: str = typer.Option(None, "--order", help="Order the trunks according to the designated fields. Availa"),
1264
+ name: str = typer.Option(None, "--name", help="Return the list of trunks matching the local gateway names"),
1265
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1266
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1267
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1268
+ debug: bool = typer.Option(False, "--debug"),
1269
+ ):
1270
+ """Get Local Gateway Call to On-Premises Extension Usage for a Trunk."""
1271
+ api = get_api(debug=debug)
1272
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/trunks/{trunk_id}/usageCallToExtension"
1273
+ params = {}
1274
+ if order is not None:
1275
+ params["order"] = order
1276
+ if name is not None:
1277
+ params["name"] = name
1278
+ if limit > 0:
1279
+ params["max"] = limit
1280
+ if offset > 0:
1281
+ params["start"] = offset
1282
+ org_id = get_org_id()
1283
+ if org_id is not None:
1284
+ params["orgId"] = org_id
1285
+ result = None
1286
+ try:
1287
+ result = api.session.rest_get(url, params=params)
1288
+ except WebexError as e:
1289
+ handle_rest_error(e)
1290
+ result = result or []
1291
+ items = result.get("locations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1292
+ if output == "json":
1293
+ print_json(items)
1294
+ else:
1295
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
1296
+
1297
+
1298
+
1299
+ @app.command("list-translation-patterns")
1300
+ def list_translation_patterns(
1301
+ limit_to_location_id: str = typer.Option(None, "--limit-to-location-id", help="When a location ID is passed, then return only the correspon"),
1302
+ limit_to_org_level_enabled: str = typer.Option(None, "--limit-to-org-level-enabled", help="When set to be `true`, then return only the organization-lev"),
1303
+ order: str = typer.Option(None, "--order", help="Sort the list of translation patterns according to translati"),
1304
+ name: str = typer.Option(None, "--name", help="Only return translation patterns with the matching `name`."),
1305
+ matching_pattern: str = typer.Option(None, "--matching-pattern", help="Only return translation patterns with the matching `matching"),
1306
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1307
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1308
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1309
+ debug: bool = typer.Option(False, "--debug"),
1310
+ ):
1311
+ """Retrieve the list of Translation Patterns."""
1312
+ api = get_api(debug=debug)
1313
+ url = f"https://webexapis.com/v1/telephony/config/callRouting/translationPatterns"
1314
+ params = {}
1315
+ if limit_to_location_id is not None:
1316
+ params["limitToLocationId"] = limit_to_location_id
1317
+ if limit_to_org_level_enabled is not None:
1318
+ params["limitToOrgLevelEnabled"] = limit_to_org_level_enabled
1319
+ if order is not None:
1320
+ params["order"] = order
1321
+ if name is not None:
1322
+ params["name"] = name
1323
+ if matching_pattern is not None:
1324
+ params["matchingPattern"] = matching_pattern
1325
+ if limit > 0:
1326
+ params["max"] = limit
1327
+ if offset > 0:
1328
+ params["start"] = offset
1329
+ org_id = get_org_id()
1330
+ if org_id is not None:
1331
+ params["orgId"] = org_id
1332
+ result = None
1333
+ try:
1334
+ result = api.session.rest_get(url, params=params)
1335
+ except WebexError as e:
1336
+ handle_rest_error(e)
1337
+ result = result or []
1338
+ items = result.get("translationPatterns", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1339
+ if output == "json":
1340
+ print_json(items)
1341
+ else:
1342
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Route Type', 'routeType')], limit=limit)
1343
+
1344
+
1345
+
1346
+ @app.command("create-translation-patterns-call-routing")
1347
+ def create_translation_patterns_call_routing(
1348
+ name: str = typer.Option(None, "--name", help="(required) Name given to a translation pattern for an organization."),
1349
+ matching_pattern: str = typer.Option(None, "--matching-pattern", help="(required) Matching pattern given to a translation pattern for an organ"),
1350
+ replacement_pattern: str = typer.Option(None, "--replacement-pattern", help="(required) Replacement pattern given to a translation pattern for an or"),
1351
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1352
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1353
+ debug: bool = typer.Option(False, "--debug"),
1354
+ ):
1355
+ """Create a Translation Pattern for an Organization."""
1356
+ api = get_api(debug=debug)
1357
+ url = f"https://webexapis.com/v1/telephony/config/callRouting/translationPatterns"
1358
+ params = {}
1359
+ org_id = get_org_id()
1360
+ if org_id is not None:
1361
+ params["orgId"] = org_id
1362
+ if json_body:
1363
+ body = json.loads(json_body)
1364
+ else:
1365
+ body = {}
1366
+ if name is not None:
1367
+ body["name"] = name
1368
+ if matching_pattern is not None:
1369
+ body["matchingPattern"] = matching_pattern
1370
+ if replacement_pattern is not None:
1371
+ body["replacementPattern"] = replacement_pattern
1372
+ _missing = [f for f in ['name', 'matchingPattern', 'replacementPattern'] if f not in body or body[f] is None]
1373
+ if _missing:
1374
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1375
+ raise typer.Exit(1)
1376
+ try:
1377
+ result = api.session.rest_post(url, json=body, params=params)
1378
+ except WebexError as e:
1379
+ handle_rest_error(e)
1380
+ if output == "json":
1381
+ print_json(result)
1382
+ elif isinstance(result, dict) and "id" in result:
1383
+ typer.echo(f"Created: {result['id']}")
1384
+ elif not result or result == {}:
1385
+ typer.echo("Created.")
1386
+ else:
1387
+ print_json(result)
1388
+
1389
+
1390
+
1391
+ @app.command("show-translation-patterns-call-routing")
1392
+ def show_translation_patterns_call_routing(
1393
+ translation_id: str = typer.Argument(help="translationId"),
1394
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1395
+ debug: bool = typer.Option(False, "--debug"),
1396
+ ):
1397
+ """Retrieve a specific Translation Pattern for an Organization."""
1398
+ api = get_api(debug=debug)
1399
+ url = f"https://webexapis.com/v1/telephony/config/callRouting/translationPatterns/{translation_id}"
1400
+ params = {}
1401
+ org_id = get_org_id()
1402
+ if org_id is not None:
1403
+ params["orgId"] = org_id
1404
+ try:
1405
+ result = api.session.rest_get(url, params=params)
1406
+ except WebexError as e:
1407
+ handle_rest_error(e)
1408
+ if output == "json":
1409
+ print_json(result)
1410
+ else:
1411
+ if isinstance(result, dict):
1412
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1413
+ elif isinstance(result, list):
1414
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1415
+ else:
1416
+ print_json(result)
1417
+
1418
+
1419
+
1420
+ @app.command("update-translation-patterns-call-routing")
1421
+ def update_translation_patterns_call_routing(
1422
+ translation_id: str = typer.Argument(help="translationId"),
1423
+ name: str = typer.Option(None, "--name", help="Name given to a translation pattern for an organization."),
1424
+ matching_pattern: str = typer.Option(None, "--matching-pattern", help="Matching pattern given to a translation pattern for an organ"),
1425
+ replacement_pattern: str = typer.Option(None, "--replacement-pattern", help="Replacement pattern given to a translation pattern for an or"),
1426
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1427
+ debug: bool = typer.Option(False, "--debug"),
1428
+ ):
1429
+ """Modify a specific Translation Pattern for an Organization."""
1430
+ api = get_api(debug=debug)
1431
+ url = f"https://webexapis.com/v1/telephony/config/callRouting/translationPatterns/{translation_id}"
1432
+ params = {}
1433
+ org_id = get_org_id()
1434
+ if org_id is not None:
1435
+ params["orgId"] = org_id
1436
+ if json_body:
1437
+ body = json.loads(json_body)
1438
+ else:
1439
+ body = {}
1440
+ if name is not None:
1441
+ body["name"] = name
1442
+ if matching_pattern is not None:
1443
+ body["matchingPattern"] = matching_pattern
1444
+ if replacement_pattern is not None:
1445
+ body["replacementPattern"] = replacement_pattern
1446
+ try:
1447
+ result = api.session.rest_put(url, json=body, params=params)
1448
+ except WebexError as e:
1449
+ handle_rest_error(e)
1450
+ typer.echo(f"Updated.")
1451
+
1452
+
1453
+
1454
+ @app.command("delete-translation-patterns-call-routing")
1455
+ def delete_translation_patterns_call_routing(
1456
+ translation_id: str = typer.Argument(help="translationId"),
1457
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1458
+ debug: bool = typer.Option(False, "--debug"),
1459
+ ):
1460
+ """Delete a specific Translation Pattern."""
1461
+ if not force:
1462
+ typer.confirm(f"Delete {translation_id}?", abort=True)
1463
+ api = get_api(debug=debug)
1464
+ url = f"https://webexapis.com/v1/telephony/config/callRouting/translationPatterns/{translation_id}"
1465
+ params = {}
1466
+ org_id = get_org_id()
1467
+ if org_id is not None:
1468
+ params["orgId"] = org_id
1469
+ try:
1470
+ api.session.rest_delete(url, params=params)
1471
+ except WebexError as e:
1472
+ handle_rest_error(e)
1473
+ typer.echo(f"Deleted: {translation_id}")
1474
+
1475
+
1476
+
1477
+ @app.command("create-translation-patterns-call-routing-1")
1478
+ def create_translation_patterns_call_routing_1(
1479
+ location_id: str = typer.Argument(help="locationId"),
1480
+ name: str = typer.Option(None, "--name", help="(required) A name given to a translation pattern for a location."),
1481
+ matching_pattern: str = typer.Option(None, "--matching-pattern", help="(required) A matching pattern given to a translation pattern for a loca"),
1482
+ replacement_pattern: str = typer.Option(None, "--replacement-pattern", help="(required) A replacement pattern given to a translation pattern for a l"),
1483
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1484
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1485
+ debug: bool = typer.Option(False, "--debug"),
1486
+ ):
1487
+ """Create a Translation Pattern for a Location."""
1488
+ api = get_api(debug=debug)
1489
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/callRouting/translationPatterns"
1490
+ params = {}
1491
+ org_id = get_org_id()
1492
+ if org_id is not None:
1493
+ params["orgId"] = org_id
1494
+ if json_body:
1495
+ body = json.loads(json_body)
1496
+ else:
1497
+ body = {}
1498
+ if name is not None:
1499
+ body["name"] = name
1500
+ if matching_pattern is not None:
1501
+ body["matchingPattern"] = matching_pattern
1502
+ if replacement_pattern is not None:
1503
+ body["replacementPattern"] = replacement_pattern
1504
+ _missing = [f for f in ['name', 'matchingPattern', 'replacementPattern'] if f not in body or body[f] is None]
1505
+ if _missing:
1506
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1507
+ raise typer.Exit(1)
1508
+ try:
1509
+ result = api.session.rest_post(url, json=body, params=params)
1510
+ except WebexError as e:
1511
+ handle_rest_error(e)
1512
+ if output == "json":
1513
+ print_json(result)
1514
+ elif isinstance(result, dict) and "id" in result:
1515
+ typer.echo(f"Created: {result['id']}")
1516
+ elif not result or result == {}:
1517
+ typer.echo("Created.")
1518
+ else:
1519
+ print_json(result)
1520
+
1521
+
1522
+
1523
+ @app.command("show-translation-patterns-call-routing-1")
1524
+ def show_translation_patterns_call_routing_1(
1525
+ location_id: str = typer.Argument(help="locationId"),
1526
+ translation_id: str = typer.Argument(help="translationId"),
1527
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1528
+ debug: bool = typer.Option(False, "--debug"),
1529
+ ):
1530
+ """Retrieve a specific Translation Pattern for a Location."""
1531
+ api = get_api(debug=debug)
1532
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/callRouting/translationPatterns/{translation_id}"
1533
+ params = {}
1534
+ org_id = get_org_id()
1535
+ if org_id is not None:
1536
+ params["orgId"] = org_id
1537
+ try:
1538
+ result = api.session.rest_get(url, params=params)
1539
+ except WebexError as e:
1540
+ handle_rest_error(e)
1541
+ if output == "json":
1542
+ print_json(result)
1543
+ else:
1544
+ if isinstance(result, dict):
1545
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1546
+ elif isinstance(result, list):
1547
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1548
+ else:
1549
+ print_json(result)
1550
+
1551
+
1552
+
1553
+ @app.command("update-translation-patterns-call-routing-1")
1554
+ def update_translation_patterns_call_routing_1(
1555
+ location_id: str = typer.Argument(help="locationId"),
1556
+ translation_id: str = typer.Argument(help="translationId"),
1557
+ name: str = typer.Option(None, "--name", help="A name given to a translation pattern for a location."),
1558
+ matching_pattern: str = typer.Option(None, "--matching-pattern", help="A matching pattern given to a translation pattern for a loca"),
1559
+ replacement_pattern: str = typer.Option(None, "--replacement-pattern", help="A replacement pattern given to a translation pattern for a l"),
1560
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1561
+ debug: bool = typer.Option(False, "--debug"),
1562
+ ):
1563
+ """Modify a specific Translation Pattern for a Location."""
1564
+ api = get_api(debug=debug)
1565
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/callRouting/translationPatterns/{translation_id}"
1566
+ params = {}
1567
+ org_id = get_org_id()
1568
+ if org_id is not None:
1569
+ params["orgId"] = org_id
1570
+ if json_body:
1571
+ body = json.loads(json_body)
1572
+ else:
1573
+ body = {}
1574
+ if name is not None:
1575
+ body["name"] = name
1576
+ if matching_pattern is not None:
1577
+ body["matchingPattern"] = matching_pattern
1578
+ if replacement_pattern is not None:
1579
+ body["replacementPattern"] = replacement_pattern
1580
+ try:
1581
+ result = api.session.rest_put(url, json=body, params=params)
1582
+ except WebexError as e:
1583
+ handle_rest_error(e)
1584
+ typer.echo(f"Updated.")
1585
+
1586
+
1587
+
1588
+ @app.command("delete-translation-patterns-call-routing-1")
1589
+ def delete_translation_patterns_call_routing_1(
1590
+ location_id: str = typer.Argument(help="locationId"),
1591
+ translation_id: str = typer.Argument(help="translationId"),
1592
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1593
+ debug: bool = typer.Option(False, "--debug"),
1594
+ ):
1595
+ """Delete a specific Translation Pattern for a Location."""
1596
+ if not force:
1597
+ typer.confirm(f"Delete {translation_id}?", abort=True)
1598
+ api = get_api(debug=debug)
1599
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/callRouting/translationPatterns/{translation_id}"
1600
+ params = {}
1601
+ org_id = get_org_id()
1602
+ if org_id is not None:
1603
+ params["orgId"] = org_id
1604
+ try:
1605
+ api.session.rest_delete(url, params=params)
1606
+ except WebexError as e:
1607
+ handle_rest_error(e)
1608
+ typer.echo(f"Deleted: {translation_id}")
1609
+
1610
+