mtncloud-cli 1.0.0

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 (499) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +20 -0
  3. data/.github/pull_request_template.md +14 -0
  4. data/.github/workflows/ci.yml +56 -0
  5. data/.github/workflows/release.yml +46 -0
  6. data/.gitignore +25 -0
  7. data/CLAUDE.md +24 -0
  8. data/CONTRIBUTING.md +91 -0
  9. data/Dockerfile +19 -0
  10. data/Gemfile +4 -0
  11. data/LICENSE.txt +23 -0
  12. data/README.md +187 -0
  13. data/Rakefile +26 -0
  14. data/bin/mcs +3 -0
  15. data/bin/mtncloud +27 -0
  16. data/lib/mtncloud/api/account_groups_interface.rb +48 -0
  17. data/lib/mtncloud/api/account_users_interface.rb +68 -0
  18. data/lib/mtncloud/api/accounts_interface.rb +9 -0
  19. data/lib/mtncloud/api/activity_interface.rb +9 -0
  20. data/lib/mtncloud/api/affinity_groups_interface.rb +46 -0
  21. data/lib/mtncloud/api/api_client.rb +1091 -0
  22. data/lib/mtncloud/api/appliance_settings_interface.rb +49 -0
  23. data/lib/mtncloud/api/approvals_interface.rb +32 -0
  24. data/lib/mtncloud/api/apps_interface.rb +180 -0
  25. data/lib/mtncloud/api/archive_buckets_interface.rb +116 -0
  26. data/lib/mtncloud/api/archive_files_interface.rb +173 -0
  27. data/lib/mtncloud/api/audit_interface.rb +9 -0
  28. data/lib/mtncloud/api/auth_interface.rb +58 -0
  29. data/lib/mtncloud/api/backup_jobs_interface.rb +13 -0
  30. data/lib/mtncloud/api/backup_restores_interface.rb +27 -0
  31. data/lib/mtncloud/api/backup_results_interface.rb +32 -0
  32. data/lib/mtncloud/api/backup_service_types_interface.rb +9 -0
  33. data/lib/mtncloud/api/backup_services_interface.rb +9 -0
  34. data/lib/mtncloud/api/backup_settings_interface.rb +20 -0
  35. data/lib/mtncloud/api/backup_types_interface.rb +9 -0
  36. data/lib/mtncloud/api/backups_interface.rb +21 -0
  37. data/lib/mtncloud/api/billing_interface.rb +34 -0
  38. data/lib/mtncloud/api/blueprints_interface.rb +70 -0
  39. data/lib/mtncloud/api/body_io.rb +22 -0
  40. data/lib/mtncloud/api/budgets_interface.rb +48 -0
  41. data/lib/mtncloud/api/catalog_item_types_interface.rb +29 -0
  42. data/lib/mtncloud/api/certificate_types_interface.rb +14 -0
  43. data/lib/mtncloud/api/certificates_interface.rb +9 -0
  44. data/lib/mtncloud/api/clients_interface.rb +41 -0
  45. data/lib/mtncloud/api/cloud_datastores_interface.rb +48 -0
  46. data/lib/mtncloud/api/cloud_folders_interface.rb +41 -0
  47. data/lib/mtncloud/api/cloud_policies_interface.rb +41 -0
  48. data/lib/mtncloud/api/cloud_resource_pools_interface.rb +66 -0
  49. data/lib/mtncloud/api/clouds_interface.rb +217 -0
  50. data/lib/mtncloud/api/clusters_interface.rb +471 -0
  51. data/lib/mtncloud/api/containers_interface.rb +145 -0
  52. data/lib/mtncloud/api/credential_types_interface.rb +9 -0
  53. data/lib/mtncloud/api/credentials_interface.rb +9 -0
  54. data/lib/mtncloud/api/custom_instance_types_interface.rb +99 -0
  55. data/lib/mtncloud/api/cypher_interface.rb +51 -0
  56. data/lib/mtncloud/api/dashboard_interface.rb +14 -0
  57. data/lib/mtncloud/api/datastores_interface.rb +21 -0
  58. data/lib/mtncloud/api/deploy_interface.rb +44 -0
  59. data/lib/mtncloud/api/deployments_interface.rb +79 -0
  60. data/lib/mtncloud/api/distributed_workers_interface.rb +41 -0
  61. data/lib/mtncloud/api/email_templates_interface.rb +53 -0
  62. data/lib/mtncloud/api/environments_interface.rb +48 -0
  63. data/lib/mtncloud/api/execute_schedules_interface.rb +71 -0
  64. data/lib/mtncloud/api/execution_request_interface.rb +27 -0
  65. data/lib/mtncloud/api/file_copy_request_interface.rb +68 -0
  66. data/lib/mtncloud/api/forgot_password_interface.rb +17 -0
  67. data/lib/mtncloud/api/group_policies_interface.rb +41 -0
  68. data/lib/mtncloud/api/groups_interface.rb +73 -0
  69. data/lib/mtncloud/api/guidance_interface.rb +44 -0
  70. data/lib/mtncloud/api/guidance_settings_interface.rb +17 -0
  71. data/lib/mtncloud/api/health_interface.rb +100 -0
  72. data/lib/mtncloud/api/host_vm_groups_interface.rb +37 -0
  73. data/lib/mtncloud/api/hub_interface.rb +25 -0
  74. data/lib/mtncloud/api/image_builder_boot_scripts_interface.rb +41 -0
  75. data/lib/mtncloud/api/image_builder_image_builds_interface.rb +73 -0
  76. data/lib/mtncloud/api/image_builder_interface.rb +20 -0
  77. data/lib/mtncloud/api/image_builder_preseed_scripts_interface.rb +41 -0
  78. data/lib/mtncloud/api/instance_types_interface.rb +20 -0
  79. data/lib/mtncloud/api/instances_interface.rb +453 -0
  80. data/lib/mtncloud/api/integration_types_interface.rb +14 -0
  81. data/lib/mtncloud/api/integrations_interface.rb +74 -0
  82. data/lib/mtncloud/api/invoice_line_items_interface.rb +9 -0
  83. data/lib/mtncloud/api/invoices_interface.rb +28 -0
  84. data/lib/mtncloud/api/jobs_interface.rb +83 -0
  85. data/lib/mtncloud/api/key_pairs_interface.rb +57 -0
  86. data/lib/mtncloud/api/library_cluster_layouts_interface.rb +47 -0
  87. data/lib/mtncloud/api/library_cluster_packages_interface.rb +60 -0
  88. data/lib/mtncloud/api/library_container_scripts_interface.rb +43 -0
  89. data/lib/mtncloud/api/library_container_templates_interface.rb +43 -0
  90. data/lib/mtncloud/api/library_container_types_interface.rb +59 -0
  91. data/lib/mtncloud/api/library_container_upgrades_interface.rb +60 -0
  92. data/lib/mtncloud/api/library_instance_types_interface.rb +74 -0
  93. data/lib/mtncloud/api/library_layouts_interface.rb +69 -0
  94. data/lib/mtncloud/api/library_operating_systems_interface.rb +63 -0
  95. data/lib/mtncloud/api/library_spec_template_types_interface.rb +43 -0
  96. data/lib/mtncloud/api/library_spec_templates_interface.rb +43 -0
  97. data/lib/mtncloud/api/license_interface.rb +30 -0
  98. data/lib/mtncloud/api/load_balancer_monitors_interface.rb +9 -0
  99. data/lib/mtncloud/api/load_balancer_pool_nodes_interface.rb +8 -0
  100. data/lib/mtncloud/api/load_balancer_pools_interface.rb +9 -0
  101. data/lib/mtncloud/api/load_balancer_pools_secondary_interface.rb +9 -0
  102. data/lib/mtncloud/api/load_balancer_profiles_interface.rb +9 -0
  103. data/lib/mtncloud/api/load_balancer_types_interface.rb +9 -0
  104. data/lib/mtncloud/api/load_balancer_virtual_servers_interface.rb +18 -0
  105. data/lib/mtncloud/api/load_balancers_interface.rb +14 -0
  106. data/lib/mtncloud/api/log_settings_interface.rb +44 -0
  107. data/lib/mtncloud/api/logs_interface.rb +37 -0
  108. data/lib/mtncloud/api/migrations_interface.rb +49 -0
  109. data/lib/mtncloud/api/monitoring_alerts_interface.rb +39 -0
  110. data/lib/mtncloud/api/monitoring_apps_interface.rb +69 -0
  111. data/lib/mtncloud/api/monitoring_checks_interface.rb +103 -0
  112. data/lib/mtncloud/api/monitoring_contacts_interface.rb +41 -0
  113. data/lib/mtncloud/api/monitoring_groups_interface.rb +77 -0
  114. data/lib/mtncloud/api/monitoring_incidents_interface.rb +107 -0
  115. data/lib/mtncloud/api/monitoring_interface.rb +35 -0
  116. data/lib/mtncloud/api/monitoring_settings_interface.rb +21 -0
  117. data/lib/mtncloud/api/network_dhcp_relays_interface.rb +36 -0
  118. data/lib/mtncloud/api/network_dhcp_servers_interface.rb +36 -0
  119. data/lib/mtncloud/api/network_domain_records_interface.rb +41 -0
  120. data/lib/mtncloud/api/network_domains_interface.rb +41 -0
  121. data/lib/mtncloud/api/network_edge_clusters_interface.rb +26 -0
  122. data/lib/mtncloud/api/network_floating_ips_interface.rb +40 -0
  123. data/lib/mtncloud/api/network_groups_interface.rb +41 -0
  124. data/lib/mtncloud/api/network_pool_ips_interface.rb +41 -0
  125. data/lib/mtncloud/api/network_pool_server_types_interface.rb +9 -0
  126. data/lib/mtncloud/api/network_pool_servers_interface.rb +48 -0
  127. data/lib/mtncloud/api/network_pools_interface.rb +41 -0
  128. data/lib/mtncloud/api/network_proxies_interface.rb +41 -0
  129. data/lib/mtncloud/api/network_routers_interface.rb +181 -0
  130. data/lib/mtncloud/api/network_security_server_types_interface.rb +9 -0
  131. data/lib/mtncloud/api/network_security_servers_interface.rb +27 -0
  132. data/lib/mtncloud/api/network_server_groups_interface.rb +7 -0
  133. data/lib/mtncloud/api/network_server_types_interface.rb +9 -0
  134. data/lib/mtncloud/api/network_servers_interface.rb +102 -0
  135. data/lib/mtncloud/api/network_services_interface.rb +63 -0
  136. data/lib/mtncloud/api/network_static_routes_interface.rb +36 -0
  137. data/lib/mtncloud/api/network_subnets_interface.rb +42 -0
  138. data/lib/mtncloud/api/network_types_interface.rb +20 -0
  139. data/lib/mtncloud/api/networks_interface.rb +48 -0
  140. data/lib/mtncloud/api/option_type_forms_interface.rb +9 -0
  141. data/lib/mtncloud/api/option_type_lists_interface.rb +53 -0
  142. data/lib/mtncloud/api/option_types_interface.rb +41 -0
  143. data/lib/mtncloud/api/options_interface.rb +17 -0
  144. data/lib/mtncloud/api/packages_interface.rb +112 -0
  145. data/lib/mtncloud/api/ping_interface.rb +22 -0
  146. data/lib/mtncloud/api/plugins_interface.rb +22 -0
  147. data/lib/mtncloud/api/policies_interface.rb +56 -0
  148. data/lib/mtncloud/api/power_schedules_interface.rb +71 -0
  149. data/lib/mtncloud/api/price_sets_interface.rb +39 -0
  150. data/lib/mtncloud/api/prices_interface.rb +71 -0
  151. data/lib/mtncloud/api/processes_interface.rb +40 -0
  152. data/lib/mtncloud/api/projects_interface.rb +33 -0
  153. data/lib/mtncloud/api/provision_types_interface.rb +24 -0
  154. data/lib/mtncloud/api/provisioning_license_types_interface.rb +31 -0
  155. data/lib/mtncloud/api/provisioning_licenses_interface.rb +60 -0
  156. data/lib/mtncloud/api/provisioning_settings_interface.rb +26 -0
  157. data/lib/mtncloud/api/read_interface.rb +24 -0
  158. data/lib/mtncloud/api/reports_interface.rb +70 -0
  159. data/lib/mtncloud/api/resource_pool_groups_interface.rb +51 -0
  160. data/lib/mtncloud/api/rest_interface.rb +37 -0
  161. data/lib/mtncloud/api/roles_interface.rb +151 -0
  162. data/lib/mtncloud/api/scale_thresholds_interface.rb +9 -0
  163. data/lib/mtncloud/api/search_interface.rb +13 -0
  164. data/lib/mtncloud/api/secondary_read_interface.rb +25 -0
  165. data/lib/mtncloud/api/secondary_rest_interface.rb +42 -0
  166. data/lib/mtncloud/api/security_group_rules_interface.rb +35 -0
  167. data/lib/mtncloud/api/security_groups_interface.rb +56 -0
  168. data/lib/mtncloud/api/security_package_types_interface.rb +9 -0
  169. data/lib/mtncloud/api/security_packages_interface.rb +9 -0
  170. data/lib/mtncloud/api/security_scans_interface.rb +9 -0
  171. data/lib/mtncloud/api/server_devices_interface.rb +30 -0
  172. data/lib/mtncloud/api/server_types_interface.rb +20 -0
  173. data/lib/mtncloud/api/servers_interface.rb +230 -0
  174. data/lib/mtncloud/api/service_catalog_interface.rb +89 -0
  175. data/lib/mtncloud/api/service_plans_interface.rb +62 -0
  176. data/lib/mtncloud/api/setup_interface.rb +46 -0
  177. data/lib/mtncloud/api/snapshots_interface.rb +19 -0
  178. data/lib/mtncloud/api/storage_datastores_interface.rb +44 -0
  179. data/lib/mtncloud/api/storage_providers_interface.rb +151 -0
  180. data/lib/mtncloud/api/storage_server_types_interface.rb +14 -0
  181. data/lib/mtncloud/api/storage_servers_interface.rb +9 -0
  182. data/lib/mtncloud/api/storage_volume_types_interface.rb +9 -0
  183. data/lib/mtncloud/api/storage_volumes_interface.rb +24 -0
  184. data/lib/mtncloud/api/subnet_types_interface.rb +20 -0
  185. data/lib/mtncloud/api/subnets_interface.rb +41 -0
  186. data/lib/mtncloud/api/support_bundles_interface.rb +46 -0
  187. data/lib/mtncloud/api/system_types_interface.rb +13 -0
  188. data/lib/mtncloud/api/systems_interface.rb +55 -0
  189. data/lib/mtncloud/api/task_sets_interface.rb +52 -0
  190. data/lib/mtncloud/api/tasks_interface.rb +66 -0
  191. data/lib/mtncloud/api/tokens_interface.rb +39 -0
  192. data/lib/mtncloud/api/usage_interface.rb +18 -0
  193. data/lib/mtncloud/api/user_groups_interface.rb +59 -0
  194. data/lib/mtncloud/api/user_settings_interface.rb +90 -0
  195. data/lib/mtncloud/api/user_sources_interface.rb +99 -0
  196. data/lib/mtncloud/api/users_interface.rb +33 -0
  197. data/lib/mtncloud/api/vdi_allocations_interface.rb +9 -0
  198. data/lib/mtncloud/api/vdi_apps_interface.rb +9 -0
  199. data/lib/mtncloud/api/vdi_gateways_interface.rb +9 -0
  200. data/lib/mtncloud/api/vdi_interface.rb +28 -0
  201. data/lib/mtncloud/api/vdi_pools_interface.rb +19 -0
  202. data/lib/mtncloud/api/virtual_images_interface.rb +210 -0
  203. data/lib/mtncloud/api/virtual_servers_interface.rb +9 -0
  204. data/lib/mtncloud/api/virtual_switches_interface.rb +9 -0
  205. data/lib/mtncloud/api/whitelabel_settings_interface.rb +57 -0
  206. data/lib/mtncloud/api/whoami_interface.rb +13 -0
  207. data/lib/mtncloud/api/wiki_interface.rb +48 -0
  208. data/lib/mtncloud/api.rb +12 -0
  209. data/lib/mtncloud/benchmarking.rb +259 -0
  210. data/lib/mtncloud/cli/cli_command.rb +2281 -0
  211. data/lib/mtncloud/cli/cli_registry.rb +337 -0
  212. data/lib/mtncloud/cli/commands/access_token_command.rb +201 -0
  213. data/lib/mtncloud/cli/commands/account_groups_command.rb +530 -0
  214. data/lib/mtncloud/cli/commands/activity_command.rb +149 -0
  215. data/lib/mtncloud/cli/commands/affinity_groups_command.rb +554 -0
  216. data/lib/mtncloud/cli/commands/alias_command.rb +278 -0
  217. data/lib/mtncloud/cli/commands/appliance_settings_command.rb +382 -0
  218. data/lib/mtncloud/cli/commands/approvals_command.rb +236 -0
  219. data/lib/mtncloud/cli/commands/apps.rb +2549 -0
  220. data/lib/mtncloud/cli/commands/archives_command.rb +2161 -0
  221. data/lib/mtncloud/cli/commands/audit.rb +188 -0
  222. data/lib/mtncloud/cli/commands/backup_jobs_command.rb +366 -0
  223. data/lib/mtncloud/cli/commands/backup_restores_command.rb +144 -0
  224. data/lib/mtncloud/cli/commands/backup_results_command.rb +149 -0
  225. data/lib/mtncloud/cli/commands/backup_services_command.rb +44 -0
  226. data/lib/mtncloud/cli/commands/backup_settings_command.rb +184 -0
  227. data/lib/mtncloud/cli/commands/backup_types_command.rb +53 -0
  228. data/lib/mtncloud/cli/commands/backups_command.rb +674 -0
  229. data/lib/mtncloud/cli/commands/benchmark_command.rb +458 -0
  230. data/lib/mtncloud/cli/commands/blueprints_command.rb +2297 -0
  231. data/lib/mtncloud/cli/commands/boot_scripts_command.rb +379 -0
  232. data/lib/mtncloud/cli/commands/budgets_command.rb +754 -0
  233. data/lib/mtncloud/cli/commands/cat_command.rb +45 -0
  234. data/lib/mtncloud/cli/commands/catalog_item_types_command.rb +858 -0
  235. data/lib/mtncloud/cli/commands/certificates_command.rb +575 -0
  236. data/lib/mtncloud/cli/commands/change_password_command.rb +132 -0
  237. data/lib/mtncloud/cli/commands/clients_command.rb +331 -0
  238. data/lib/mtncloud/cli/commands/cloud_datastores_command.rb +543 -0
  239. data/lib/mtncloud/cli/commands/cloud_folders_command.rb +512 -0
  240. data/lib/mtncloud/cli/commands/cloud_resource_pools_command.rb +870 -0
  241. data/lib/mtncloud/cli/commands/clouds.rb +2037 -0
  242. data/lib/mtncloud/cli/commands/clouds_types.rb +140 -0
  243. data/lib/mtncloud/cli/commands/cluster_types.rb +179 -0
  244. data/lib/mtncloud/cli/commands/clusters.rb +5855 -0
  245. data/lib/mtncloud/cli/commands/coloring_command.rb +82 -0
  246. data/lib/mtncloud/cli/commands/containers_command.rb +967 -0
  247. data/lib/mtncloud/cli/commands/credential_types_command.rb +36 -0
  248. data/lib/mtncloud/cli/commands/credentials_command.rb +124 -0
  249. data/lib/mtncloud/cli/commands/curl_command.rb +168 -0
  250. data/lib/mtncloud/cli/commands/cypher_command.rb +587 -0
  251. data/lib/mtncloud/cli/commands/dashboard_command.rb +300 -0
  252. data/lib/mtncloud/cli/commands/debug_command.rb +75 -0
  253. data/lib/mtncloud/cli/commands/deploy.rb +485 -0
  254. data/lib/mtncloud/cli/commands/deployments.rb +923 -0
  255. data/lib/mtncloud/cli/commands/deploys.rb +485 -0
  256. data/lib/mtncloud/cli/commands/distributed_workers_command.rb +282 -0
  257. data/lib/mtncloud/cli/commands/echo_command.rb +61 -0
  258. data/lib/mtncloud/cli/commands/edit_profile_command.rb +57 -0
  259. data/lib/mtncloud/cli/commands/edit_rc_command.rb +77 -0
  260. data/lib/mtncloud/cli/commands/email_templates_command.rb +373 -0
  261. data/lib/mtncloud/cli/commands/environments_command.rb +401 -0
  262. data/lib/mtncloud/cli/commands/execute_schedules_command.rb +735 -0
  263. data/lib/mtncloud/cli/commands/execution_request_command.rb +333 -0
  264. data/lib/mtncloud/cli/commands/exit_command.rb +34 -0
  265. data/lib/mtncloud/cli/commands/file_copy_request_command.rb +352 -0
  266. data/lib/mtncloud/cli/commands/forgot_password.rb +133 -0
  267. data/lib/mtncloud/cli/commands/get_prompt_command.rb +36 -0
  268. data/lib/mtncloud/cli/commands/groups.rb +818 -0
  269. data/lib/mtncloud/cli/commands/guidance_command.rb +529 -0
  270. data/lib/mtncloud/cli/commands/guidance_settings.rb +148 -0
  271. data/lib/mtncloud/cli/commands/health_command.rb +1046 -0
  272. data/lib/mtncloud/cli/commands/history_command.rb +60 -0
  273. data/lib/mtncloud/cli/commands/host_vm_groups_command.rb +423 -0
  274. data/lib/mtncloud/cli/commands/hosts.rb +2986 -0
  275. data/lib/mtncloud/cli/commands/hub.rb +215 -0
  276. data/lib/mtncloud/cli/commands/image_builder_command.rb +1197 -0
  277. data/lib/mtncloud/cli/commands/instance_types.rb +163 -0
  278. data/lib/mtncloud/cli/commands/instances.rb +5813 -0
  279. data/lib/mtncloud/cli/commands/integrations_command.rb +1412 -0
  280. data/lib/mtncloud/cli/commands/invoices_command.rb +1151 -0
  281. data/lib/mtncloud/cli/commands/jobs_command.rb +998 -0
  282. data/lib/mtncloud/cli/commands/key_pairs.rb +470 -0
  283. data/lib/mtncloud/cli/commands/library_cluster_layouts_command.rb +901 -0
  284. data/lib/mtncloud/cli/commands/library_cluster_packages_command.rb +552 -0
  285. data/lib/mtncloud/cli/commands/library_container_scripts_command.rb +446 -0
  286. data/lib/mtncloud/cli/commands/library_container_templates_command.rb +526 -0
  287. data/lib/mtncloud/cli/commands/library_container_types_command.rb +698 -0
  288. data/lib/mtncloud/cli/commands/library_forms_command.rb +625 -0
  289. data/lib/mtncloud/cli/commands/library_instance_types_command.rb +668 -0
  290. data/lib/mtncloud/cli/commands/library_layouts_command.rb +849 -0
  291. data/lib/mtncloud/cli/commands/library_operating_systems_command.rb +671 -0
  292. data/lib/mtncloud/cli/commands/library_option_lists_command.rb +503 -0
  293. data/lib/mtncloud/cli/commands/library_option_types_command.rb +329 -0
  294. data/lib/mtncloud/cli/commands/library_spec_templates_command.rb +547 -0
  295. data/lib/mtncloud/cli/commands/library_upgrades_command.rb +596 -0
  296. data/lib/mtncloud/cli/commands/license.rb +475 -0
  297. data/lib/mtncloud/cli/commands/load_balancer_monitors.rb +70 -0
  298. data/lib/mtncloud/cli/commands/load_balancer_pool_nodes.rb +87 -0
  299. data/lib/mtncloud/cli/commands/load_balancer_pools.rb +129 -0
  300. data/lib/mtncloud/cli/commands/load_balancer_profiles.rb +64 -0
  301. data/lib/mtncloud/cli/commands/load_balancer_types.rb +38 -0
  302. data/lib/mtncloud/cli/commands/load_balancer_virtual_servers.rb +147 -0
  303. data/lib/mtncloud/cli/commands/load_balancers.rb +189 -0
  304. data/lib/mtncloud/cli/commands/log_level_command.rb +54 -0
  305. data/lib/mtncloud/cli/commands/log_settings_command.rb +443 -0
  306. data/lib/mtncloud/cli/commands/login.rb +148 -0
  307. data/lib/mtncloud/cli/commands/logout.rb +63 -0
  308. data/lib/mtncloud/cli/commands/logs_command.rb +140 -0
  309. data/lib/mtncloud/cli/commands/man_command.rb +409 -0
  310. data/lib/mtncloud/cli/commands/migrations.rb +453 -0
  311. data/lib/mtncloud/cli/commands/monitoring_alerts_command.rb +791 -0
  312. data/lib/mtncloud/cli/commands/monitoring_apps_command.rb +716 -0
  313. data/lib/mtncloud/cli/commands/monitoring_checks_command.rb +766 -0
  314. data/lib/mtncloud/cli/commands/monitoring_contacts_command.rb +339 -0
  315. data/lib/mtncloud/cli/commands/monitoring_groups_command.rb +674 -0
  316. data/lib/mtncloud/cli/commands/monitoring_incidents_command.rb +835 -0
  317. data/lib/mtncloud/cli/commands/monitoring_settings.rb +212 -0
  318. data/lib/mtncloud/cli/commands/network_dhcp_relays_command.rb +375 -0
  319. data/lib/mtncloud/cli/commands/network_dhcp_servers_command.rb +366 -0
  320. data/lib/mtncloud/cli/commands/network_domains_command.rb +896 -0
  321. data/lib/mtncloud/cli/commands/network_edge_clusters_command.rb +288 -0
  322. data/lib/mtncloud/cli/commands/network_firewalls_command.rb +783 -0
  323. data/lib/mtncloud/cli/commands/network_floating_ips.rb +147 -0
  324. data/lib/mtncloud/cli/commands/network_groups_command.rb +654 -0
  325. data/lib/mtncloud/cli/commands/network_pool_server_types.rb +20 -0
  326. data/lib/mtncloud/cli/commands/network_pool_servers_command.rb +318 -0
  327. data/lib/mtncloud/cli/commands/network_pools_command.rb +903 -0
  328. data/lib/mtncloud/cli/commands/network_proxies_command.rb +578 -0
  329. data/lib/mtncloud/cli/commands/network_routers_command.rb +2628 -0
  330. data/lib/mtncloud/cli/commands/network_security_server_types.rb +20 -0
  331. data/lib/mtncloud/cli/commands/network_server_groups_command.rb +222 -0
  332. data/lib/mtncloud/cli/commands/network_server_types_command.rb +20 -0
  333. data/lib/mtncloud/cli/commands/network_servers_command.rb +398 -0
  334. data/lib/mtncloud/cli/commands/network_services_command.rb +198 -0
  335. data/lib/mtncloud/cli/commands/network_static_routes_command.rb +451 -0
  336. data/lib/mtncloud/cli/commands/network_transport_zones_command.rb +412 -0
  337. data/lib/mtncloud/cli/commands/networks_command.rb +1692 -0
  338. data/lib/mtncloud/cli/commands/open_command.rb +30 -0
  339. data/lib/mtncloud/cli/commands/options.rb +231 -0
  340. data/lib/mtncloud/cli/commands/packages_command.rb +822 -0
  341. data/lib/mtncloud/cli/commands/ping.rb +242 -0
  342. data/lib/mtncloud/cli/commands/plugins.rb +214 -0
  343. data/lib/mtncloud/cli/commands/policies_command.rb +990 -0
  344. data/lib/mtncloud/cli/commands/power_schedules_command.rb +731 -0
  345. data/lib/mtncloud/cli/commands/preseed_scripts_command.rb +378 -0
  346. data/lib/mtncloud/cli/commands/price_sets_command.rb +557 -0
  347. data/lib/mtncloud/cli/commands/prices_command.rb +692 -0
  348. data/lib/mtncloud/cli/commands/processes_command.rb +469 -0
  349. data/lib/mtncloud/cli/commands/projects_command.rb +537 -0
  350. data/lib/mtncloud/cli/commands/provisioning_licenses_command.rb +509 -0
  351. data/lib/mtncloud/cli/commands/provisioning_settings_command.rb +238 -0
  352. data/lib/mtncloud/cli/commands/recent_activity_command.rb +171 -0
  353. data/lib/mtncloud/cli/commands/remote.rb +1818 -0
  354. data/lib/mtncloud/cli/commands/reports_command.rb +724 -0
  355. data/lib/mtncloud/cli/commands/resource_pool_groups_command.rb +586 -0
  356. data/lib/mtncloud/cli/commands/rm_command.rb +14 -0
  357. data/lib/mtncloud/cli/commands/roles.rb +3120 -0
  358. data/lib/mtncloud/cli/commands/scale_thresholds.rb +103 -0
  359. data/lib/mtncloud/cli/commands/search_command.rb +184 -0
  360. data/lib/mtncloud/cli/commands/security_group_rules.rb +230 -0
  361. data/lib/mtncloud/cli/commands/security_groups.rb +1294 -0
  362. data/lib/mtncloud/cli/commands/security_package_types.rb +32 -0
  363. data/lib/mtncloud/cli/commands/security_packages.rb +84 -0
  364. data/lib/mtncloud/cli/commands/security_scans.rb +107 -0
  365. data/lib/mtncloud/cli/commands/self_service_command.rb +17 -0
  366. data/lib/mtncloud/cli/commands/server_devices_command.rb +207 -0
  367. data/lib/mtncloud/cli/commands/service_catalog_command.rb +1528 -0
  368. data/lib/mtncloud/cli/commands/service_plans_command.rb +895 -0
  369. data/lib/mtncloud/cli/commands/set_prompt_command.rb +51 -0
  370. data/lib/mtncloud/cli/commands/setup.rb +423 -0
  371. data/lib/mtncloud/cli/commands/shell.rb +717 -0
  372. data/lib/mtncloud/cli/commands/sleep_command.rb +43 -0
  373. data/lib/mtncloud/cli/commands/snapshots.rb +156 -0
  374. data/lib/mtncloud/cli/commands/source_command.rb +30 -0
  375. data/lib/mtncloud/cli/commands/ssl_verification_command.rb +47 -0
  376. data/lib/mtncloud/cli/commands/storage_datastores.rb +457 -0
  377. data/lib/mtncloud/cli/commands/storage_providers_command.rb +1401 -0
  378. data/lib/mtncloud/cli/commands/storage_server_types.rb +45 -0
  379. data/lib/mtncloud/cli/commands/storage_servers.rb +116 -0
  380. data/lib/mtncloud/cli/commands/storage_volume_types.rb +45 -0
  381. data/lib/mtncloud/cli/commands/storage_volumes.rb +236 -0
  382. data/lib/mtncloud/cli/commands/subnets_command.rb +819 -0
  383. data/lib/mtncloud/cli/commands/support_bundles_command.rb +606 -0
  384. data/lib/mtncloud/cli/commands/systems.rb +1203 -0
  385. data/lib/mtncloud/cli/commands/tasks.rb +1391 -0
  386. data/lib/mtncloud/cli/commands/tee_command.rb +14 -0
  387. data/lib/mtncloud/cli/commands/tenants_command.rb +315 -0
  388. data/lib/mtncloud/cli/commands/tokens_command.rb +391 -0
  389. data/lib/mtncloud/cli/commands/update_command.rb +46 -0
  390. data/lib/mtncloud/cli/commands/usage_command.rb +203 -0
  391. data/lib/mtncloud/cli/commands/user_groups_command.rb +512 -0
  392. data/lib/mtncloud/cli/commands/user_settings_command.rb +849 -0
  393. data/lib/mtncloud/cli/commands/user_sources_command.rb +1065 -0
  394. data/lib/mtncloud/cli/commands/users.rb +897 -0
  395. data/lib/mtncloud/cli/commands/vdi_allocations_command.rb +159 -0
  396. data/lib/mtncloud/cli/commands/vdi_apps_command.rb +317 -0
  397. data/lib/mtncloud/cli/commands/vdi_command.rb +359 -0
  398. data/lib/mtncloud/cli/commands/vdi_gateways_command.rb +290 -0
  399. data/lib/mtncloud/cli/commands/vdi_pools_command.rb +574 -0
  400. data/lib/mtncloud/cli/commands/version_command.rb +38 -0
  401. data/lib/mtncloud/cli/commands/view.rb +107 -0
  402. data/lib/mtncloud/cli/commands/virtual_images.rb +1362 -0
  403. data/lib/mtncloud/cli/commands/virtual_switches_command.rb +336 -0
  404. data/lib/mtncloud/cli/commands/whitelabel_settings_command.rb +653 -0
  405. data/lib/mtncloud/cli/commands/whoami.rb +323 -0
  406. data/lib/mtncloud/cli/commands/wiki_command.rb +448 -0
  407. data/lib/mtncloud/cli/commands/workflows.rb +1022 -0
  408. data/lib/mtncloud/cli/credentials.rb +482 -0
  409. data/lib/mtncloud/cli/dot_file.rb +180 -0
  410. data/lib/mtncloud/cli/error_handler.rb +307 -0
  411. data/lib/mtncloud/cli/errors.rb +53 -0
  412. data/lib/mtncloud/cli/expression_parser.rb +153 -0
  413. data/lib/mtncloud/cli/mixins/accounts_helper.rb +484 -0
  414. data/lib/mtncloud/cli/mixins/affinity_helper.rb +118 -0
  415. data/lib/mtncloud/cli/mixins/backups_helper.rb +200 -0
  416. data/lib/mtncloud/cli/mixins/deployments_helper.rb +163 -0
  417. data/lib/mtncloud/cli/mixins/execution_request_helper.rb +50 -0
  418. data/lib/mtncloud/cli/mixins/infrastructure_helper.rb +966 -0
  419. data/lib/mtncloud/cli/mixins/jobs_helper.rb +202 -0
  420. data/lib/mtncloud/cli/mixins/library_helper.rb +539 -0
  421. data/lib/mtncloud/cli/mixins/load_balancers_helper.rb +176 -0
  422. data/lib/mtncloud/cli/mixins/logs_helper.rb +131 -0
  423. data/lib/mtncloud/cli/mixins/monitoring_helper.rb +876 -0
  424. data/lib/mtncloud/cli/mixins/networks_helper.rb +62 -0
  425. data/lib/mtncloud/cli/mixins/operations_helper.rb +41 -0
  426. data/lib/mtncloud/cli/mixins/option_source_helper.rb +254 -0
  427. data/lib/mtncloud/cli/mixins/print_helper.rb +1631 -0
  428. data/lib/mtncloud/cli/mixins/processes_helper.rb +286 -0
  429. data/lib/mtncloud/cli/mixins/prompt_helper.rb +136 -0
  430. data/lib/mtncloud/cli/mixins/provisioning_helper.rb +2963 -0
  431. data/lib/mtncloud/cli/mixins/remote_helper.rb +139 -0
  432. data/lib/mtncloud/cli/mixins/rest_command.rb +994 -0
  433. data/lib/mtncloud/cli/mixins/secondary_rest_command.rb +700 -0
  434. data/lib/mtncloud/cli/mixins/storage_servers_helper.rb +93 -0
  435. data/lib/mtncloud/cli/mixins/storage_volumes_helper.rb +76 -0
  436. data/lib/mtncloud/cli/mixins/vdi_helper.rb +246 -0
  437. data/lib/mtncloud/cli/mixins/whoami_helper.rb +73 -0
  438. data/lib/mtncloud/cli/option_parser.rb +127 -0
  439. data/lib/mtncloud/cli/option_types.rb +1622 -0
  440. data/lib/mtncloud/cli/version.rb +6 -0
  441. data/lib/mtncloud/cli.rb +105 -0
  442. data/lib/mtncloud/currency.rb +184 -0
  443. data/lib/mtncloud/ext/hash.rb +109 -0
  444. data/lib/mtncloud/ext/nil_class.rb +5 -0
  445. data/lib/mtncloud/ext/rest_client.rb +37 -0
  446. data/lib/mtncloud/ext/string.rb +66 -0
  447. data/lib/mtncloud/formatters.rb +511 -0
  448. data/lib/mtncloud/logging.rb +185 -0
  449. data/lib/mtncloud/morpkg.rb +119 -0
  450. data/lib/mtncloud/rest_client.rb +104 -0
  451. data/lib/mtncloud/routes.rb +304 -0
  452. data/lib/mtncloud/terminal.rb +528 -0
  453. data/lib/mtncloud/util.rb +29 -0
  454. data/lib/mtncloud.rb +5 -0
  455. data/mtncloud-cli.gemspec +38 -0
  456. data/test/api/affinity_groups_interface_test.rb +43 -0
  457. data/test/api/clients_interface_test.rb +23 -0
  458. data/test/api/clouds_interface_test.rb +67 -0
  459. data/test/api/containers_interface_test.rb +68 -0
  460. data/test/api/host_vm_groups_interface_test.rb +35 -0
  461. data/test/api/instances_interface_test.rb +22 -0
  462. data/test/api/network_routers_interface_test.rb +22 -0
  463. data/test/api/systems_interface_test.rb +26 -0
  464. data/test/api/whoami_interface_test.rb +14 -0
  465. data/test/branding_test.rb +160 -0
  466. data/test/cli/access_token_test.rb +36 -0
  467. data/test/cli/affinity_groups_cloud_cluster_scope_test.rb +270 -0
  468. data/test/cli/affinity_groups_cluster_scope_test.rb +171 -0
  469. data/test/cli/affinity_groups_test.rb +57 -0
  470. data/test/cli/affinity_groups_violations_scope_test.rb +141 -0
  471. data/test/cli/affinity_helper_test.rb +367 -0
  472. data/test/cli/auth_test.rb +88 -0
  473. data/test/cli/backups_test.rb +212 -0
  474. data/test/cli/cli_test.rb +48 -0
  475. data/test/cli/clients_test.rb +45 -0
  476. data/test/cli/clusters_uuid_test.rb +146 -0
  477. data/test/cli/containers_test.rb +92 -0
  478. data/test/cli/distributed_workers_test.rb +68 -0
  479. data/test/cli/help_test.rb +25 -0
  480. data/test/cli/host_vm_groups_cluster_scope_test.rb +187 -0
  481. data/test/cli/host_vm_groups_test.rb +63 -0
  482. data/test/cli/infrastructure_helper_cluster_resolver_test.rb +250 -0
  483. data/test/cli/instances_test.rb +37 -0
  484. data/test/cli/man_test.rb +14 -0
  485. data/test/cli/migrations_test.rb +39 -0
  486. data/test/cli/network_routers_test.rb +65 -0
  487. data/test/cli/remote_test.rb +89 -0
  488. data/test/cli/roles_test.rb +47 -0
  489. data/test/cli/shell_test.rb +81 -0
  490. data/test/cli/storage_volumes_test.rb +79 -0
  491. data/test/cli/systems_test.rb +206 -0
  492. data/test/cli/version_test.rb +23 -0
  493. data/test/cli/view_test.rb +55 -0
  494. data/test/cli/whoami_test.rb +17 -0
  495. data/test/mtncloud_test.rb +16 -0
  496. data/test/test_case.rb +342 -0
  497. data/test/test_config.rb +137 -0
  498. data/test/test_data_helper.rb +97 -0
  499. metadata +807 -0
@@ -0,0 +1,1622 @@
1
+ require 'term/ansicolor'
2
+ require 'readline'
3
+
4
+ module MtnCloud
5
+ module Cli
6
+ module OptionTypes
7
+ include Term::ANSIColor
8
+ # include MtnCloud::Cli::PrintHelper
9
+
10
+ def self.confirm(message,options={})
11
+ if options[:yes] == true
12
+ return true
13
+ end
14
+ default_value = options[:default]
15
+ value_found = false
16
+ while value_found == false do
17
+ # if default_value.nil?
18
+ # print "#{message} (yes/no): "
19
+ # else
20
+ # print "#{message} (yes/no) [#{!!default_value ? 'yes' : 'no'}]: "
21
+ # end
22
+ # input = $stdin.gets.chomp!
23
+
24
+ # should use Readline.readline to probably
25
+ Readline.completion_append_character = ""
26
+ Readline.basic_word_break_characters = ''
27
+ Readline.completer_word_break_characters = '' rescue nil
28
+ Readline.completion_proc = nil
29
+ if default_value.nil?
30
+ confirm_prompt = "#{message} (yes/no): "
31
+ else
32
+ confirm_prompt = "#{message} (yes/no) [#{!!default_value ? 'yes' : 'no'}]: "
33
+ end
34
+ input = Readline.readline(confirm_prompt, false).to_s
35
+ input = input.chomp.strip
36
+
37
+ if input.empty? && !default_value.nil?
38
+ return !!default_value
39
+ end
40
+ if input.downcase == 'yes' || input.downcase == 'y'
41
+ return true
42
+ elsif input.downcase == 'no' || input.downcase == 'n'
43
+ return false
44
+ else
45
+ puts "Invalid Option... Please try again."
46
+ end
47
+ end
48
+ end
49
+
50
+ # supresses prompting unless --prompt has been passed
51
+ def self.no_prompt(option_types, options={}, api_client=nil,api_params={})
52
+ options[:edit_mode] = true # hack used for updates to avoid default values being used
53
+ if options[:always_prompt]
54
+ prompt(option_types, options, api_client, api_params)
55
+ else
56
+ prompt(option_types, options, api_client, api_params, true)
57
+ end
58
+ end
59
+
60
+ def self.prompt(option_types, options={}, api_client=nil, api_params={}, no_prompt=false, paging_enabled=false, ignore_empty=false, skip_sort = false)
61
+ paging_enabled = false if MtnCloud::Cli.windows?
62
+ no_prompt = no_prompt || options[:no_prompt]
63
+ results = {}
64
+ options = options || {}
65
+
66
+ # inject cli only stuff into option_types (should clone() here)
67
+ option_types.each do |option_type|
68
+ if options[:help_field_prefix]
69
+ option_type[:help_field_prefix] = options[:help_field_prefix]
70
+ end
71
+ # a lot of optionTypes have fieldGroup:'Options' instead of 'default'
72
+ if option_type['fieldGroup'].to_s.downcase == 'options'
73
+ option_type['fieldGroup'] = 'default'
74
+ end
75
+ # apply custom templates
76
+ if option_type['fieldName'] == 'sshHosts'
77
+ option_type['type'] = 'multiText'
78
+ end
79
+ # swap types to multiSelect when flag is set..
80
+ config_multi_select = option_type["config"] && ["true","on"].include?(option_type["config"]["multiSelect"].to_s)
81
+ if config_multi_select
82
+ if option_type["type"] == "typeahead"
83
+ option_type["type"] = "multiTypeahead"
84
+ elsif option_type["type"] == "select"
85
+ option_type["type"] = "multiSelect"
86
+ elsif option_type["type"] == "textarea"
87
+ option_type["type"] = "multiText"
88
+ end
89
+ end
90
+ end
91
+
92
+ # puts "Options Prompt #{options}"
93
+ # Sort options by default, group, advanced
94
+ # add displayOrder if it's missing, so it doesn't end up using a random order
95
+ # if !option_types.find {|it| it['displayOrder'] && it['displayOrder'] != 0 }
96
+ # option_types.each_with_index {|it, i| it['displayOrder'] = i+1 }
97
+ # end
98
+ cur_field_group = 'default'
99
+ prompt_local_credentials = true
100
+ # reject help only options..
101
+ option_types.reject! {|it| it[:for_help_only]}
102
+ # sort options
103
+ if !skip_sort
104
+ option_types = self.sort_option_types(option_types)
105
+ end
106
+ option_types.each do |option_type|
107
+ next if option_type['localCredential'] && !prompt_local_credentials
108
+ context_map = results
109
+ value = nil
110
+ value_found = false
111
+
112
+ # allow for mapping of domain to relevant type: domain.zone => router.zone
113
+ option_type['fieldContext'] = (options[:context_map] || {})[option_type['fieldContext']] || option_type['fieldContext']
114
+ field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
115
+ help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
116
+ namespaces = field_key.split(".")
117
+ field_name = namespaces.pop
118
+
119
+
120
+ # support --no-options --skip-option x,y,z --only-option x,y,z
121
+ if options[:no_options]
122
+ next
123
+ elsif options[:skip_options] && options[:skip_options].find {|it| it.to_s.downcase == option_type['fieldName'].to_s.downcase || it.to_s.downcase == option_type['fieldLabel'].to_s.downcase }
124
+ next
125
+ elsif options[:only_options] && !options[:only_options].find {|it| it.to_s.downcase == option_type['fieldName'].to_s.downcase || it.to_s.downcase == option_type['fieldLabel'].to_s.downcase }
126
+ next
127
+ end
128
+
129
+
130
+ field_group = (option_type['fieldGroup'] || 'default').to_s.sub(/options\Z/i, "").strip # avoid "ADVANCED OPTION OPTIONS"
131
+
132
+ if cur_field_group != field_group
133
+ cur_field_group = field_group
134
+ if !no_prompt && option_type['noPrompt'] != true
135
+ print "\n#{cur_field_group.upcase} OPTIONS\n#{"=" * ("#{cur_field_group} OPTIONS".length)}\n\n"
136
+ end
137
+ end
138
+
139
+ # How about this instead?
140
+ # option_type = option_type.clone
141
+ # field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
142
+ # if field_key != ''
143
+ # value = get_object_value(options, field_key)
144
+ # if value != nil && options[:always_prompt] != true
145
+ # value_found = true
146
+ # end
147
+ # end
148
+
149
+
150
+ # respect optionType.dependsOnCode
151
+ # i guess this switched to visibleOnCode, respect one or the other
152
+ visible_option_check_value = option_type['dependsOnCode']
153
+ if !option_type['visibleOnCode'].to_s.empty?
154
+ visible_option_check_value = option_type['visibleOnCode']
155
+ end
156
+ # adding a slight hack for dependencies on the network component since its not an option type
157
+ extra_option_params = {}
158
+ if visible_option_check_value == 'networkInterfaces'
159
+ # extra_option_params["networkInterfaceIds[]"] = []
160
+ elsif !visible_option_check_value.to_s.empty?
161
+ match_type = 'any'
162
+
163
+ if visible_option_check_value.include?('::')
164
+ match_type = 'all' if visible_option_check_value.start_with?('matchAll')
165
+ visible_option_check_value = visible_option_check_value[visible_option_check_value.index('::') + 2..-1]
166
+ end
167
+
168
+ found_dep_value = match_type == 'all' ? true : false
169
+ visible_option_check_value.sub(',', ' ').split(' ').each do |value|
170
+ parts = value.split(':')
171
+ depends_on_code = parts[0]
172
+ depends_on_value = parts.count > 1 ? parts[1].to_s.strip : nil
173
+ depends_on_option_type = option_types.find {|it| it["code"] == depends_on_code }
174
+ if !depends_on_option_type
175
+ depends_on_option_type = option_types.find {|it|
176
+ (it['fieldContext'] ? "#{it['fieldContext']}.#{it['fieldName']}" : it['fieldName']) == depends_on_code
177
+ }
178
+ end
179
+
180
+ depends_on_field_key = depends_on_code
181
+ if depends_on_option_type
182
+ depends_on_field_key = depends_on_option_type['fieldContext'].nil? || depends_on_option_type['fieldContext'].empty? ? "#{depends_on_option_type['fieldName']}" : "#{depends_on_option_type['fieldContext']}.#{depends_on_option_type['fieldName']}"
183
+ end
184
+
185
+ field_value = get_object_value(results, depends_on_field_key) ||
186
+ get_object_value(options, depends_on_field_key) ||
187
+ get_object_value(api_params, depends_on_field_key)
188
+
189
+ if field_value.nil? && !options['_object_key'].nil?
190
+ field_value = get_object_value({options['_object_key'] => results}, depends_on_field_key)
191
+ end
192
+
193
+ if !field_value.nil? && (depends_on_value.nil? || depends_on_value.empty? || field_value.to_s.match?(depends_on_value))
194
+ found_dep_value = true if match_type != 'all'
195
+ else
196
+ found_dep_value = false if match_type == 'all'
197
+ end
198
+ end
199
+ next if !found_dep_value
200
+ end
201
+
202
+ # respect optionType.requireOnCode
203
+ require_option_check_value = option_type['requireOnCode']
204
+ if !require_option_check_value.to_s.empty?
205
+ # require_on_code = check_require_on_code(option_type, option_types, options)
206
+
207
+ match_type = 'any'
208
+
209
+ if require_option_check_value.include?('::')
210
+ match_type = 'all' if require_option_check_value.start_with?('matchAll')
211
+ require_option_check_value = require_option_check_value[require_option_check_value.index('::') + 2..-1]
212
+ end
213
+
214
+ found_dep_value = match_type == 'all' ? true : false
215
+ require_option_check_value.sub(',', ' ').split(' ').each do |value|
216
+ parts = value.split(':')
217
+ depends_on_code = parts[0]
218
+ depends_on_value = parts.count > 1 ? parts[1].to_s.strip : nil
219
+ depends_on_option_type = option_types.find {|it| it["code"] == depends_on_code }
220
+ if !depends_on_option_type
221
+ depends_on_option_type = option_types.find {|it|
222
+ (it['fieldContext'] ? "#{it['fieldContext']}.#{it['fieldName']}" : it['fieldName']) == depends_on_code
223
+ }
224
+ end
225
+
226
+ depends_on_field_key = depends_on_code
227
+ if depends_on_option_type
228
+ depends_on_field_key = depends_on_option_type['fieldContext'].nil? || depends_on_option_type['fieldContext'].empty? ? "#{depends_on_option_type['fieldName']}" : "#{depends_on_option_type['fieldContext']}.#{depends_on_option_type['fieldName']}"
229
+ end
230
+
231
+ field_value = get_object_value(results, depends_on_field_key) ||
232
+ get_object_value(options, depends_on_field_key) ||
233
+ get_object_value(api_params, depends_on_field_key)
234
+
235
+ if field_value.nil? && !options['_object_key'].nil?
236
+ field_value = get_object_value({options['_object_key'] => results}, depends_on_field_key)
237
+ end
238
+
239
+ if !field_value.nil? && (depends_on_value.nil? || depends_on_value.empty? || field_value.to_s.match?(depends_on_value))
240
+ found_dep_value = true if match_type != 'all'
241
+ else
242
+ found_dep_value = false if match_type == 'all'
243
+ end
244
+ end
245
+
246
+ option_type = option_type.merge({'required' => found_dep_value})
247
+ end
248
+
249
+ # build parameters for option source api request
250
+ option_params = (option_type['noParams'] ? {} : (api_params || {}).deep_merge(results))
251
+ option_params.merge!(option_type['optionParams']) if option_type['optionParams']
252
+ # option_params.merge!(extra_option_params) if extra_option_params && !extra_option_params.empty?
253
+
254
+ cur_namespace = options
255
+ parent_context_map = context_map
256
+ parent_ns = field_name
257
+
258
+ namespaces.each do |ns|
259
+ next if ns.empty?
260
+ parent_context_map = context_map
261
+ parent_ns = ns
262
+ cur_namespace[ns.to_s] ||= {}
263
+ cur_namespace = cur_namespace[ns.to_s]
264
+ context_map[ns.to_s] ||= {}
265
+ context_map = context_map[ns.to_s]
266
+ end
267
+
268
+ # CLI only options that need some do some inflection to decide how to prompt
269
+ # defaultValue is it right now..
270
+ if option_type[:preprocesser].is_a?(Proc)
271
+ option_type[:preprocesser].call(option_type, api_client, option_params)
272
+ end
273
+
274
+ # credential type
275
+ handle_credential_type = -> {
276
+ credential_type = select_prompt(option_type.merge({'defaultValue' => value}), api_client, option_params.merge({'credentialTypes' => option_type['config']['credentialTypes']}), !value.nil?, nil, paging_enabled, ignore_empty, options[:edit_mode])
277
+ # continue prompting for local creds
278
+ if credential_type == 'local'
279
+ parent_context_map.reject! {|k,v| k == 'credential'}
280
+ next
281
+ end
282
+ # hide local cred options
283
+ prompt_local_credentials = false
284
+ if credential_type.is_a?(Numeric)
285
+ # set as credential.id
286
+ credential = {'id' => credential_type}
287
+ else
288
+ # prompt credential type options
289
+ credential = prompt(api_client.credential_types.list({name:credential_type})['credentialTypes'][0]['optionTypes'], options, api_client, option_params, options[:no_prompt], paging_enabled, ignore_empty)['credential']
290
+ credential['type'] = credential_type
291
+ end
292
+ parent_context_map['credential'] = credential
293
+ }
294
+
295
+ # use the value passed in the options map
296
+ if cur_namespace.respond_to?('key?') && cur_namespace.key?(field_name)
297
+ value = cur_namespace[field_name]
298
+ input_value = ['select', 'multiSelect','typeahead', 'multiTypeahead'].include?(option_type['type']) && option_type['fieldInput'] ? cur_namespace[option_type['fieldInput']] : nil
299
+ if option_type['type'] == 'number'
300
+ if !value.to_s.empty?
301
+ value = value.to_s.include?('.') ? value.to_f : value.to_i
302
+ end
303
+ # these select prompts should just fall down through below, with the extra params no_prompt, use_value
304
+ elsif option_type['type'] == 'select'
305
+ value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true, nil, false, ignore_empty, options[:edit_mode])
306
+ elsif option_type['type'] == 'multiSelect'
307
+ # support value as csv like "thing1, thing2"
308
+ value_list = value.is_a?(String) ? value.split(",").collect {|v| v ? v.to_s.strip : v } : [value].flatten
309
+ input_value_list = input_value.is_a?(String) ? input_value.split(",").collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
310
+ select_value_list = []
311
+ value_list.each_with_index do |v, i|
312
+ select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true, nil, false, ignore_empty, options[:edit_mode])
313
+ end
314
+ value = select_value_list
315
+ elsif option_type['type'] == 'typeahead'
316
+ value = typeahead_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true)
317
+ elsif option_type['type'] == 'multiTypeahead'
318
+ # support value as csv like "thing1, thing2"
319
+ value_list = value.is_a?(String) ? value.split(",").collect {|v| v ? v.to_s.strip : v } : [value].flatten
320
+ input_value_list = input_value.is_a?(String) ? input_value.split(",").collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
321
+ select_value_list = []
322
+ value_list.each_with_index do |v, i|
323
+ select_value_list << typeahead_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true)
324
+ end
325
+ value = select_value_list
326
+ elsif option_type['type'] == 'credential'
327
+ handle_credential_type.call
328
+ end
329
+ if options[:always_prompt] != true
330
+ value_found = true
331
+ end
332
+ end
333
+
334
+ # set the value that has been passed to the option type default value
335
+ if value != nil # && value != ''
336
+ option_type = option_type.clone
337
+ option_type['defaultValue'] = value
338
+ end
339
+ # no_prompt means skip prompting and instead
340
+ # use default value or error if a required option is not present
341
+ if no_prompt || option_type['noPrompt'] == true
342
+ if !value_found
343
+ if option_type['defaultValue'] != nil && !['select', 'multiSelect','typeahead','multiTypeahead'].include?(option_type['type'])
344
+ value = option_type['defaultValue']
345
+ value_found = true
346
+ end
347
+ if !value_found
348
+ # select type is special because it supports skipSingleOption
349
+ # and prints the available options on error
350
+ if option_type['type'] == 'azureMarketplace'
351
+ value = azure_marketplace_prompt(option_type, options, api_client, option_params)
352
+ # inject {marketplacePublisher:'...',} into config, not as config.azureMarketplace = {}
353
+ # and remove any passed in values from
354
+ if value.is_a?(Hash)
355
+ context_map.merge!(value)
356
+ end
357
+ next
358
+ end
359
+ if ['select', 'multiSelect'].include?(option_type['type'])
360
+ value = select_prompt(option_type, api_client, option_params, true, nil, false, ignore_empty, options[:edit_mode])
361
+ value_found = !!value
362
+ end
363
+ if ['typeahead', 'multiTypeahead'].include?(option_type['type'])
364
+ value = typeahead_prompt(option_type, api_client, option_params, true)
365
+ value_found = !!value
366
+ end
367
+ if option_type['type'] == 'hidden'
368
+ if option_type['optionSource'].nil?
369
+ value = option_type['defaultValue']
370
+ else
371
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, option_params)
372
+ config_multi_select = option_type["config"] && ["true","on"].include?(option_type["config"]["multiSelect"].to_s)
373
+ if config_multi_select
374
+ value = select_options.collect { |it| it['value'] }
375
+ elsif select_options.is_a?(Array)
376
+ value = select_options[0] ? select_options[0]['value'] : nil
377
+ elsif select_options.is_a?(Hash)
378
+ value = select_options['value']
379
+ else
380
+ value = select_options
381
+ end
382
+ end
383
+ end
384
+ if !value_found && !ignore_empty
385
+ if option_type['required']
386
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
387
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{help_field_key}=] - #{option_type['description']}\n", Term::ANSIColor.reset
388
+ print "\n"
389
+ exit 1
390
+ else
391
+ parent_context_map.reject! {|k,v| k == parent_ns && (v.nil? || (v.is_a?(Hash) && v.empty?))}
392
+ next
393
+ end
394
+ end
395
+ end
396
+ end
397
+ end
398
+
399
+ if !value_found
400
+ if option_type['type'] == 'text'
401
+ value = generic_prompt(option_type)
402
+ elsif option_type['type'] == 'number'
403
+ value = number_prompt(option_type)
404
+ elsif option_type['type'] == 'password'
405
+ value = password_prompt(option_type)
406
+ elsif option_type['type'] == 'checkbox'
407
+ value = checkbox_prompt(option_type)
408
+ if options[:checkbox_as_boolean]
409
+ value = (value == 'on')
410
+ end
411
+ elsif option_type['type'] == 'radio'
412
+ value = radio_prompt(option_type)
413
+ elsif option_type['type'] == 'textarea'
414
+ value = multiline_prompt(option_type)
415
+ elsif option_type['type'] == 'code-editor'
416
+ value = multiline_prompt(option_type)
417
+ elsif option_type['type'] == 'credential'
418
+ print "\nCREDENTIALS\n#{"=" * ("CREDENTIALS".length)}\n\n"
419
+ handle_credential_type.call
420
+ elsif ['select', 'multiSelect'].include?(option_type['type'])
421
+ # so, the /api/options/source is may need ALL the previously
422
+ # selected values that are being accumulated in options
423
+ # api_params is just extra params to always send
424
+ # I suppose the entered value should take precedence
425
+ # api_params = api_params.merge(options) # this might be good enough
426
+ # dup it
427
+ value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty, options[:edit_mode])
428
+ if value && option_type['type'] == 'multiSelect'
429
+ value = [value]
430
+ recommended_count = (option_type['config'] || {})['recommendedCount'] || 0
431
+ while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => recommended_count > value.count}) do
432
+ if addn_value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty, options[:edit_mode])
433
+ value << addn_value
434
+ else
435
+ break
436
+ end
437
+ end
438
+ end
439
+ elsif ['typeahead', 'multiTypeahead'].include?(option_type['type'])
440
+ value = typeahead_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled)
441
+ if value && option_type['type'] == 'multiTypeahead'
442
+ value = [value]
443
+ while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
444
+ if addn_value = typeahead_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled)
445
+ value << addn_value
446
+ else
447
+ break
448
+ end
449
+ end
450
+ end
451
+ elsif option_type['type'] == 'hidden'
452
+ if option_type['optionSource'].nil?
453
+ value = option_type['defaultValue']
454
+ else
455
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, option_params)
456
+ config_multi_select = option_type["config"] && ["true","on"].include?(option_type["config"]["multiSelect"].to_s)
457
+ if config_multi_select
458
+ value = select_options.collect { |it| it['value'] }
459
+ elsif select_options.is_a?(Array)
460
+ value = select_options[0] ? select_options[0]['value'] : nil
461
+ elsif select_options.is_a?(Hash)
462
+ value = select_options['value']
463
+ else
464
+ value = select_options
465
+ end
466
+ end
467
+ elsif option_type['type'] == 'file'
468
+ value = file_prompt(option_type)
469
+ elsif option_type['type'] == 'file-content' || option_type['type'] == 'fileContent'
470
+ value = file_content_prompt(option_type, options, api_client, {})
471
+ elsif option_type['type'] == 'logoSelector'
472
+ value = file_prompt(option_type)
473
+ elsif option_type['type'] == 'multiText'
474
+ value = multitext_prompt(option_type)
475
+ elsif option_type['type'] == 'azureMarketplace'
476
+ value = azure_marketplace_prompt(option_type, options, api_client, option_params)
477
+ # inject {marketplacePublisher:'...',} into config, not as config.azureMarketplace = {}
478
+ # and remove any passed in values from
479
+ if value.is_a?(Hash)
480
+ context_map.merge!(value)
481
+ end
482
+ next
483
+ else
484
+ value = generic_prompt(option_type)
485
+ end
486
+ end
487
+ # --labels x,y,z uses processValue proc to convert strings to an array
488
+ if option_type['processValue'].is_a?(Proc)
489
+ value = option_type['processValue'].call(value)
490
+ end
491
+
492
+ if option_type['type'] == 'multiSelect'
493
+ value = [value] if !value.nil? && !value.is_a?(Array)
494
+ elsif option_type['type'] == 'multiText'
495
+ # multiText expects csv value
496
+ if value && value.is_a?(String)
497
+ value = value.split(",").collect {|it| it.strip }
498
+ end
499
+ # todo: Handle these types added with the new form fields:
500
+ #
501
+ # byteSize
502
+ # code-editor
503
+ # fileContent
504
+ # logoSelector
505
+ # keyValue
506
+ # textArray
507
+ # typeahead
508
+ # group
509
+ # cloud
510
+ # environment
511
+ # diskManager
512
+ # layout
513
+ # networkManager
514
+ # plan
515
+ # resourcePool
516
+ # secGroup
517
+ # tag
518
+ # httpHeader
519
+ elsif option_type['type'] == 'byteSize'
520
+ if value.to_s.empty?
521
+ value = 0 # nil
522
+ elsif value.is_a?(String)
523
+ if value.to_s.upcase.include?("G")
524
+ value = value.to_i * 1024 * 1024 * 1024
525
+ elsif value.to_s.upcase.include?("M")
526
+ value = value * 1024 * 1024
527
+ else
528
+ # assume bytes by default..
529
+ value = value.to_i
530
+ end
531
+ end
532
+ elsif option_type['type'] == 'keyValue'
533
+ value = try_as_json(value)
534
+ if value.is_a?(String)
535
+ map = {}
536
+ value.split(",").each do |it|
537
+ pair = it.split("=");
538
+ map[pair[0].to_s.strip] = pair[1..-1].join("=").strip
539
+ end
540
+ value = map
541
+ end
542
+ elsif option_type['type'] == 'textArray'
543
+ value = try_as_json(value)
544
+ if value.is_a?(String)
545
+ value = value.split(",").collect {|it| it.to_s.strip }
546
+ end
547
+ else
548
+ # default translation
549
+ # for non text inputs, try to parse value as JSON
550
+ # if option_type['type'] == 'group' || option_type['type'] == 'cloud' etc..
551
+ if value.is_a?(String) && option_type['type'] != 'text'
552
+ value = try_as_json(value)
553
+ end
554
+ end
555
+ context_map[field_name] = value if !(value.nil? || (value.is_a?(Hash) && value.empty?))
556
+ parent_context_map.reject! {|k,v| k == parent_ns && (v.nil? || (v.is_a?(Hash) && v.empty?))}
557
+ end
558
+ results
559
+ end
560
+
561
+ def self.radio_prompt(option_type)
562
+ value_found = false
563
+ value = nil
564
+ options = []
565
+ if option_type['config'] and option_type['config']['radioOptions']
566
+ option_type['config']['radioOptions'].each do |radio_option|
567
+ options << {key: radio_option['key'], checked: radio_option['checked']}
568
+ end
569
+ end
570
+ optionString = options.collect{ |b| b[:checked] ? "(#{b[:key]})" : b[:key]}.join(', ')
571
+ while !value_found do
572
+ print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }[#{optionString}]: "
573
+ input = $stdin.gets.chomp!
574
+ if input == '?'
575
+ help_prompt(option_type)
576
+ else
577
+ if input.nil? || input.empty?
578
+ selectedOption = options.find{|o| o[:checked] == true}
579
+ else
580
+ selectedOption = options.find{|o| o[:key].downcase == input.downcase}
581
+ end
582
+ if selectedOption
583
+ value = selectedOption[:key]
584
+ else
585
+ puts "Invalid Option. Please select from #{optionString}."
586
+ end
587
+ if !value.nil? || option_type['required'] != true
588
+ value_found = true
589
+ end
590
+ end
591
+ end
592
+ return value
593
+ end
594
+
595
+ def self.number_prompt(option_type)
596
+ value_found = false
597
+ value = nil
598
+ while !value_found do
599
+ print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
600
+ input = $stdin.gets.chomp!
601
+ value = input.empty? ? option_type['defaultValue'] : input
602
+ if !value.to_s.empty?
603
+ value = value.to_s.include?('.') ? value.to_f : value.to_i
604
+ end
605
+ if input == '?'
606
+ help_prompt(option_type)
607
+ elsif !value.nil? || option_type['required'] != true
608
+ value_found = true
609
+ end
610
+ end
611
+ return value
612
+ end
613
+
614
+ def self.set_last_select(obj)
615
+ Thread.current[:_last_select] = obj
616
+ end
617
+
618
+ def self.get_last_select()
619
+ Thread.current[:_last_select]
620
+ end
621
+
622
+ def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false, ignore_empty=false, edit_mode=false)
623
+ paging_enabled = false if MtnCloud::Cli.windows?
624
+ field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
625
+ help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
626
+ value_found = false
627
+ value = nil
628
+ value_field = (option_type['config'] ? option_type['config']['valueField'] : nil) || 'value'
629
+ default_value = option_type['defaultValue']
630
+ default_value = default_value['id'] if default_value && default_value.is_a?(Hash) && !default_value['id'].nil?
631
+
632
+ if !option_type['params'].nil?
633
+ api_params = (api_params || {}).select {|k,v| option_type['params'].key?(k) || option_type['params'].key?(k.to_s)}
634
+ option_type['params'].select {|k,v| !v.empty?}.each {|k,v| api_params[k] = v}
635
+ end
636
+
637
+ # local array of options
638
+ if option_type['selectOptions']
639
+ # calculate from inline lambda
640
+ if option_type['selectOptions'].is_a?(Proc)
641
+ select_options = option_type['selectOptions'].call(api_client, api_params || {})
642
+ else
643
+ # todo: better type validation
644
+ select_options = option_type['selectOptions']
645
+ end
646
+ elsif option_type['optionSource']
647
+ # calculate from inline lambda
648
+ if option_type['optionSource'].is_a?(Proc)
649
+ select_options = option_type['optionSource'].call(api_client, api_params || {})
650
+ elsif option_type['optionSource'] == 'list'
651
+ # /api/options/list is a special action for custom OptionTypeLists, just need to pass the optionTypeId parameter
652
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, (api_params || {}).merge({'optionTypeId' => option_type['id']}))
653
+ else
654
+ # remote optionSource aka /api/options/$optionSource?
655
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
656
+ end
657
+ else
658
+ raise "option '#{help_field_key}' is type: 'select' and missing selectOptions or optionSource!"
659
+ end
660
+
661
+ return nil if (select_options.nil? || select_options.count == 0) && ignore_empty
662
+
663
+ # ensure the preselected value (passed as an option) is in the dropdown
664
+ if !use_value.nil?
665
+ matched_option = select_options.find {|it| it[value_field].to_s == use_value.to_s }
666
+ if matched_option.nil?
667
+ matched_options = select_options.select {|it| opt['name'].to_s == use_value.to_s }
668
+ if matched_options.size > 1
669
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{use_value}]\n\n", Term::ANSIColor.reset
670
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
671
+ if matched_options && matched_options.size > 10
672
+ display_select_options(option_type, matched_options.first(10))
673
+ puts " (#{matched_options.size-10} more)"
674
+ else
675
+ display_select_options(option_type, matched_options)
676
+ end
677
+ print "The value '#{input}' matched #{matched_options.size()} options.\n"
678
+ # print "Perhaps you meant one of these? #{ored_list(matched_options.collect {|i|i[value_field]}, 3)}\n"
679
+ print "Try using value instead of name.\n"
680
+ print "\n"
681
+ exit 1
682
+ elsif matched_options.size == 1
683
+ matched_option = matched_options[0]
684
+ end
685
+ end
686
+ if !matched_option.nil?
687
+ value = matched_option[value_field]
688
+ value_found = true
689
+ else
690
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{use_value}]\n\n", Term::ANSIColor.reset
691
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
692
+ if select_options && select_options.size > 10
693
+ display_select_options(option_type, select_options.first(10))
694
+ puts " (#{select_options.size-10} more)"
695
+ else
696
+ display_select_options(option_type, select_options)
697
+ end
698
+ print "\n"
699
+ exit 1
700
+ end
701
+ elsif edit_mode
702
+ # do not use a default value for edit mode
703
+ # skipSingleOption is no longer supported
704
+ # elsif !select_options.nil? && select_options.count == 1 && option_type['skipSingleOption'] == true
705
+ # value_found = true
706
+ # value = select_options[0]['value']
707
+ # if there is just one option, use it as the defaultValue
708
+ elsif !select_options.nil? && select_options.count == 1
709
+ if option_type['required'] && default_value.nil?
710
+ default_value = select_options[0]['name'] # name is prettier than value
711
+ end
712
+ elsif !select_options.nil?
713
+ if default_value.nil?
714
+ found_default_option = select_options.find {|opt| opt['isDefault'] == true }
715
+ if found_default_option
716
+ default_value = found_default_option['name'] # name is prettier than value
717
+ end
718
+ else
719
+ found_default_option = select_options.find {|opt| opt[value_field].to_s == default_value.to_s || opt['name'] == default_value.to_s}
720
+ #found_default_option = select_options.find {|opt| opt[value_field].to_s.start_with?(default_value.to_s) || opt['name'].to_s.start_with?(default_value.to_s)} if !found_default_option
721
+ if found_default_option
722
+ default_value = found_default_option['name'] # name is prettier than value
723
+ end
724
+ end
725
+ end
726
+ # default to the first option
727
+ first_option = select_options ? select_options.find {|opt| opt['isGroup'] != true } : nil
728
+ if !value_found && default_value.nil? && option_type['defaultFirstOption'] && first_option
729
+ # default_value = first_option[value_field]
730
+ # nicer to display name instead, it will match and replace with value
731
+ default_value = first_option['name'] ? first_option['name'] : first_option[value_field]
732
+ end
733
+
734
+ if no_prompt
735
+ if !value_found
736
+ if default_value == ""
737
+ default_value = nil
738
+ end
739
+ if !default_value.nil? && !select_options.nil?
740
+ matched_option = select_options.find {|it| it[value_field].to_s == default_value.to_s }
741
+ if matched_option.nil?
742
+ matched_options = select_options.select {|it| it['name'].to_s == default_value.to_s }
743
+ if matched_options.size > 1
744
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{default_value}]\n\n", Term::ANSIColor.reset
745
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
746
+ if matched_options && matched_options.size > 10
747
+ display_select_options(option_type, matched_options.first(10))
748
+ puts " (#{matched_options.size-10} more)"
749
+ else
750
+ display_select_options(option_type, matched_options)
751
+ end
752
+ print "The value '#{default_value}' matched #{matched_options.size()} options.\n"
753
+ # print "Perhaps you meant one of these? #{ored_list(matched_options.collect {|i|i[value_field]}, 3)}\n"
754
+ print "Try using value instead of name.\n"
755
+ print "\n"
756
+ exit 1
757
+ elsif matched_options.size == 1
758
+ matched_option = matched_options[0]
759
+ end
760
+ end
761
+ if !matched_option.nil?
762
+ value = matched_option[value_field]
763
+ value_found = true
764
+ else
765
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{default_value}]\n\n", Term::ANSIColor.reset
766
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
767
+ if select_options && select_options.size > 10
768
+ display_select_options(option_type, select_options.first(10))
769
+ puts " (#{select_options.size-10} more)"
770
+ else
771
+ display_select_options(option_type, select_options)
772
+ end
773
+ print "\n"
774
+ exit 1
775
+ end
776
+ elsif !select_options.nil? && select_options.count > 0 && option_type['autoPickOption'] == true
777
+ value_found = true
778
+ value = select_options[0][value_field]
779
+ elsif option_type['required']
780
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
781
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{help_field_key}=] - #{option_type['description']}\n", Term::ANSIColor.reset
782
+ if select_options && select_options.size > 10
783
+ display_select_options(option_type, select_options.first(10))
784
+ puts " (#{select_options.size-10} more)"
785
+ else
786
+ display_select_options(option_type, select_options)
787
+ end
788
+ print "\n"
789
+ exit 1
790
+ else
791
+ return nil
792
+ end
793
+ end
794
+ end
795
+
796
+ paging = nil
797
+ if paging_enabled
798
+ option_count = select_options ? select_options.count : 0
799
+ page_size = Readline.get_screen_size[0] - 6
800
+ if page_size < option_count
801
+ paging = {:cur_page => 0, :page_size => page_size, :total => option_count}
802
+ end
803
+ end
804
+
805
+ while !value_found do
806
+ #Readline.input = $stdin
807
+ Readline.completion_append_character = ""
808
+ Readline.basic_word_break_characters = ''
809
+ Readline.completer_word_break_characters = '' rescue nil
810
+ Readline.completion_proc = proc {|s|
811
+ matches = []
812
+ available_options = (select_options || [])
813
+ available_options.each{|option|
814
+ if option['name'] && option['name'] =~ /^#{Regexp.escape(s)}/
815
+ matches << option['name']
816
+ # elsif option['id'] && option['id'].to_s =~ /^#{Regexp.escape(s)}/
817
+ elsif option[value_field] && option[value_field].to_s == s
818
+ matches << option['name']
819
+ end
820
+ }
821
+ matches
822
+ }
823
+
824
+ has_more_pages = paging && (paging[:cur_page] * paging[:page_size]) < paging[:total]
825
+ input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!default_value.to_s.empty? ? ' ['+default_value.to_s+']' : ''} ['?' for#{has_more_pages && paging[:cur_page] > 0 ? ' more ' : ' '}options]: ", false).to_s
826
+ input = input.chomp.strip
827
+ if input.empty? && default_value
828
+ input = default_value.to_s
829
+ end
830
+ matched_option = (select_options || []).find{|it| (!it['value'].nil? && it['value'].to_s == input) || (!it[value_field].nil? && it[value_field].to_s == input) || (it[value_field].nil? && input.empty?)}
831
+ if matched_option.nil?
832
+ matched_options = (select_options || []).select {|it| it['name'] == input } # should probably be case insensitive
833
+ if matched_options.size > 1
834
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{input}]\n\n", Term::ANSIColor.reset
835
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
836
+ if matched_options && matched_options.size > 10
837
+ display_select_options(option_type, matched_options.first(10))
838
+ puts " (#{matched_options.size-10} more)"
839
+ else
840
+ display_select_options(option_type, matched_options)
841
+ end
842
+ print "The value '#{input}' matched #{matched_options.size()} options.\n"
843
+ # print "Perhaps you meant one of these? #{ored_list(matched_options.collect {|i|i[value_field]}, 3)}\n"
844
+ print "Try using value instead of name.\n"
845
+ print "\n"
846
+ #exit 1
847
+ elsif matched_options.size == 1
848
+ matched_option = matched_options[0]
849
+ end
850
+ end
851
+
852
+ if matched_option
853
+ value = matched_option[value_field]
854
+ set_last_select(matched_option)
855
+ elsif !input.nil? && !input.to_s.empty?
856
+ input = '?'
857
+ end
858
+
859
+ if input == '?'
860
+ help_prompt(option_type)
861
+ display_select_options(option_type, (select_options || []), paging)
862
+ if paging
863
+ paging[:cur_page] = (paging[:cur_page] + 1) * paging[:page_size] < paging[:total] ? paging[:cur_page] + 1 : 0
864
+ end
865
+ elsif !value.nil? || option_type['required'] != true
866
+ value_found = true
867
+ end
868
+ end
869
+
870
+ # wrap in object when using fieldInput
871
+ if value && !option_type['fieldInput'].nil?
872
+ value = {option_type['fieldName'].split('.').last => value, option_type['fieldInput'] => (no_prompt ? option_type['defaultInputValue'] : field_input_prompt(option_type))}
873
+ end
874
+
875
+ if value && !option_type['resultValueField'].nil?
876
+ value = {option_type['resultValueField'] => value}
877
+ end
878
+ value
879
+ end
880
+
881
+ # this works like select_prompt, but refreshes options with ?query=value between inputs
882
+ # paging_enabled is ignored right now
883
+ def self.typeahead_prompt(option_type,api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false)
884
+ paging_enabled = false if MtnCloud::Cli.windows?
885
+ paging = nil
886
+ select_options = nil
887
+ field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
888
+ help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
889
+ input = ""
890
+ value_found = false
891
+ value = nil
892
+ value_field = (option_type['config'] ? option_type['config']['valueField'] : nil) || 'value'
893
+ default_value = option_type['defaultValue']
894
+ default_value = default_value['id'] if default_value && default_value.is_a?(Hash) && !default_value['id'].nil?
895
+
896
+ while !value_found do
897
+ # ok get input, refresh options and see if it matches
898
+ # if matches one, cool otherwise print matches and reprompt or error
899
+ if use_value
900
+ input = use_value
901
+ elsif no_prompt
902
+ input = default_value
903
+ else
904
+ #Readline.input = $stdin
905
+ Readline.completion_append_character = ""
906
+ Readline.basic_word_break_characters = ''
907
+ Readline.completer_word_break_characters = '' rescue nil
908
+ Readline.completion_proc = proc {|s|
909
+ matches = []
910
+ available_options = (select_options || [])
911
+ available_options.each{|option|
912
+ if option['name'] && option['name'] =~ /^#{Regexp.escape(s)}/
913
+ matches << option['name']
914
+ # elsif option['id'] && option['id'].to_s =~ /^#{Regexp.escape(s)}/
915
+ elsif option[value_field] && option[value_field].to_s == s
916
+ matches << option['name']
917
+ end
918
+ }
919
+ matches
920
+ }
921
+ # prompt for typeahead input value
922
+ has_more_pages = paging && ((paging[:cur_page] + 1) * paging[:page_size]) < paging[:total]
923
+ input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!default_value.to_s.empty? ? ' ['+default_value.to_s+']' : ''} ['?' for#{has_more_pages ? ' more ' : ' '}options]: ", false).to_s
924
+ input = input.chomp.strip
925
+ end
926
+
927
+ # just hit enter, use [default] if set
928
+ if input.empty? && default_value
929
+ input = default_value.to_s
930
+ end
931
+
932
+ # not required and no value? ok proceed
933
+ if input.to_s == "" && option_type['required'] != true
934
+ value_found = true
935
+ value = nil # or "" # hmm
936
+ #next
937
+ break
938
+ end
939
+
940
+ # required and no value? you need help
941
+ # if input.to_s == "" && option_type['required'] == true
942
+ # help_prompt(option_type)
943
+ # display_select_options(option_type, select_options) unless select_options.empty?
944
+ # next
945
+ # end
946
+
947
+ # looking for help with this input
948
+ if input == '?'
949
+ help_prompt(option_type)
950
+ select_options = select_options || load_options(option_type, api_client, api_params)
951
+
952
+ if !select_options.empty?
953
+ if paging_enabled
954
+ if paging.nil?
955
+ option_count = select_options ? select_options.count : 0
956
+ page_size = Readline.get_screen_size[0] - 6
957
+ if page_size < option_count
958
+ paging = {:cur_page => 0, :page_size => page_size, :total => option_count}
959
+ end
960
+ else
961
+ paging[:cur_page] = (paging[:cur_page] + 1) * paging[:page_size] < paging[:total] ? paging[:cur_page] + 1 : 0
962
+ end
963
+ end
964
+ display_select_options(option_type, select_options, paging)
965
+ end
966
+ next
967
+ end
968
+
969
+ # just hit enter? scram
970
+ # looking for help with this input
971
+ # if input == ""
972
+ # help_prompt(option_type)
973
+ # display_select_options(option_type, select_options)
974
+ # next
975
+ # end
976
+
977
+ # this is how typeahead works, it keeps refreshing the options with a new ?query={value}
978
+ # query_value = (value || use_value || default_value || '')
979
+ query_value = (input || '')
980
+ api_params ||= {}
981
+ search_parameter = option_type['searchParameter'] || 'query'
982
+ api_params[search_parameter] = query_value
983
+ # skip refresh if you just hit enter
984
+ if !query_value.empty? || (select_options.nil? || select_options.empty?)
985
+ select_options = load_options(option_type, api_client, api_params, query_value)
986
+ end
987
+
988
+ # match input to option name or value
989
+ # actually that is redundant, it should already be filtered to matches
990
+ # and can just do this:
991
+ # select_option = select_options.size == 1 ? select_options[0] : nil
992
+ select_option = select_options.find{|b| (b[value_field] && (b[value_field].to_s == input.to_s)) || ((b[value_field].nil? || b[value_field] == "") && (input == "")) }
993
+ if select_option.nil?
994
+ select_option = select_options.find{|b| b['name'] && b['name'] == input }
995
+ end
996
+
997
+ # found matching value, else did not find a value, show matching options and prompt again or error
998
+ if select_option
999
+ value = select_option[value_field]
1000
+ set_last_select(select_option)
1001
+ value_found = true
1002
+ else
1003
+ if use_value || no_prompt
1004
+ # todo: make this nicer
1005
+ # help_prompt(option_type)
1006
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
1007
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{help_field_key}=] - #{option_type['description']}\n", Term::ANSIColor.reset
1008
+ if select_options && select_options.size > 10
1009
+ display_select_options(option_type, select_options.first(10))
1010
+ puts " (#{select_options.size-10} more)"
1011
+ else
1012
+ display_select_options(option_type, select_options)
1013
+ end
1014
+ print "\n"
1015
+ if select_options.empty?
1016
+ print "The value '#{input}' matched 0 options.\n"
1017
+ # print "Please try again.\n"
1018
+ elsif select_options.size() == 1
1019
+ print "The value '#{input}' matched 1 option.\n"
1020
+ print "Perhaps you meant '#{select_options[0]['name']}' instead?"
1021
+ # print "Please try again.\n"
1022
+ else
1023
+ print "The value '#{input}' matched #{select_options.size()} options.\n"
1024
+ print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
1025
+ # print "Please try again.\n"
1026
+ end
1027
+ print "\n"
1028
+ exit 1
1029
+ else
1030
+ #help_prompt(option_type)
1031
+ if select_options && select_options.size > 10
1032
+ display_select_options(option_type, select_options.first(10))
1033
+ puts " (#{select_options.size-10} more)"
1034
+ else
1035
+ display_select_options(option_type, select_options)
1036
+ end
1037
+ print "\n"
1038
+ if select_options.empty?
1039
+ print "The value '#{input}' matched 0 options.\n"
1040
+ print "Please try again.\n"
1041
+ elsif select_options.size() == 1
1042
+ print "The value '#{input}' matched 1 option.\n"
1043
+ print "Perhaps you meant '#{select_options[0]['name']}' instead?"
1044
+ else
1045
+ print "The value '#{input}' matched #{select_options.size()} options.\n"
1046
+ print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
1047
+ print "Please try again.\n"
1048
+ end
1049
+ print "\n"
1050
+ # reprompting now...
1051
+ end
1052
+ end
1053
+ end # end while !value_found
1054
+
1055
+ # wrap in object when using fieldInput
1056
+ if value && !option_type['fieldInput'].nil?
1057
+ value = {option_type['fieldName'].split('.').last => value, option_type['fieldInput'] => (no_prompt ? option_type['defaultInputValue'] : field_input_prompt(option_type))}
1058
+ end
1059
+ value
1060
+ end
1061
+
1062
+ def self.checkbox_prompt(option_type)
1063
+ value_found = false
1064
+ value = nil
1065
+ has_default = option_type['defaultValue'] != nil
1066
+ default_on = has_default ? ['on', 'true', 'yes', '1'].include?(option_type['defaultValue'].to_s.downcase) : false
1067
+ while !value_found do
1068
+ print "#{option_type['fieldLabel']} (on/off)#{has_default ? ' ['+(default_on ? 'on' : 'off')+']' : ''}: "
1069
+ input = $stdin.gets.chomp!
1070
+ if input == '?'
1071
+ help_prompt(option_type)
1072
+ next
1073
+ end
1074
+ check_value = input.downcase.strip
1075
+ if check_value == 'yes' || check_value == 'y' || check_value == 'on' || check_value == 'true' || check_value == '1'
1076
+ value_found = true
1077
+ value = 'on'
1078
+ elsif check_value == 'no' || check_value == 'n' || check_value == 'off' || check_value == 'true' || check_value == '0'
1079
+ value_found = true
1080
+ value = 'off'
1081
+ elsif input == '' && has_default
1082
+ value_found = true
1083
+ value = default_on ? 'on' : 'off'
1084
+ elsif input != ""
1085
+ puts "Invalid Option... Please try again."
1086
+ next
1087
+ end
1088
+ if value.nil? && option_type['required']
1089
+ puts "Invalid Option... Please try again."
1090
+ next
1091
+ end
1092
+ if value.nil? && !option_type['required']
1093
+ value_found = true
1094
+ end
1095
+ end
1096
+ return value
1097
+ end
1098
+
1099
+ def self.field_input_prompt(option_type)
1100
+ value_found = false
1101
+ value = nil
1102
+
1103
+ input_field_label = option_type['fieldInput'].gsub(/[A-Z]/, ' \0').split(' ').collect {|it| it.capitalize}.join(' ')
1104
+ input_field_name = option_type['fieldName'].split('.').reverse.drop(1).reverse.push(option_type['fieldInput']).join('.')
1105
+ input_option_type = option_type.merge({'fieldName' => input_field_name, 'fieldLabel' => input_field_label, 'required' => true, 'type' => 'text'})
1106
+
1107
+ while !value_found do
1108
+ print "#{input_field_label}#{option_type['defaultInputValue'] ? " [#{option_type['defaultInputValue']}]" : ''}: "
1109
+ input = $stdin.gets.chomp!
1110
+ value = input.empty? ? option_type['defaultInputValue'] : input
1111
+ if input == '?'
1112
+ help_prompt(input_option_type)
1113
+ elsif !value.nil?
1114
+ value_found = true
1115
+ end
1116
+ end
1117
+ return value
1118
+ end
1119
+
1120
+ def self.generic_prompt(option_type)
1121
+ value_found = false
1122
+ value = nil
1123
+ while !value_found do
1124
+ # print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
1125
+ # input = $stdin.gets.chomp!
1126
+ Readline.completion_append_character = ""
1127
+ Readline.basic_word_break_characters = ''
1128
+ Readline.completer_word_break_characters = '' rescue nil
1129
+ Readline.completion_proc = nil
1130
+ prompt_label = "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
1131
+ input = Readline.readline(prompt_label, false).to_s
1132
+ input = input.chomp #.strip
1133
+
1134
+ value = input.empty? ? option_type['defaultValue'] : input
1135
+ if input == '?'
1136
+ help_prompt(option_type)
1137
+ elsif !value.nil? || option_type['required'] != true
1138
+ value_found = true
1139
+ end
1140
+ # attempt to parse Java regex and validate it
1141
+ if option_type["verifyPattern"].to_s != "" && !(value.to_s == "" && option_type['required'])
1142
+ begin
1143
+ # pattern is matched on the entire string
1144
+ verify_pattern = Regexp.compile("^" + option_type["verifyPattern"] + "$")
1145
+ if !verify_pattern.match(value)
1146
+ value_found = false
1147
+ puts "Invalid Option. Value must match the pattern '#{option_type['verifyPattern']}'. Please try again."
1148
+ next
1149
+ end
1150
+ rescue => regex_ex
1151
+ puts "Failed to parse verifyPattern '#{option_type['verifyPattern']}' as a regular expression"
1152
+ end
1153
+ end
1154
+ end
1155
+ return value
1156
+ end
1157
+
1158
+ def self.multiline_prompt(option_type)
1159
+ value_found = false
1160
+ value = nil
1161
+ while !value_found do
1162
+ if value.nil?
1163
+ print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)} [Type 'EOF' to stop input]: \n"
1164
+ end
1165
+ input = $stdin.gets.chomp!
1166
+ # value = input.empty? ? option_type['defaultValue'] : input
1167
+ if input == '?' && value.nil?
1168
+ help_prompt(option_type)
1169
+ elsif input.chomp == '' && value.nil?
1170
+ # just hit enter right away to skip this
1171
+ value_found = true
1172
+ elsif input.chomp == 'EOF'
1173
+ value_found = true
1174
+ else
1175
+ if value.nil?
1176
+ value = ''
1177
+ end
1178
+ value << input + "\n"
1179
+ end
1180
+ end
1181
+ return value ? value.strip : value
1182
+ end
1183
+
1184
+ def self.password_prompt(option_type)
1185
+ value_found = false
1186
+ while !value_found do
1187
+ # readline is still echoing secret with 'NUL:'' so just use $stdin on windows
1188
+ # and some other environments? just use noecho unless running unit tests
1189
+ if MtnCloud::Cli.windows? || !MtnCloud::Cli.testing?
1190
+ print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+'************'+']' : ''}: "
1191
+ input = $stdin.noecho(&:gets).chomp!
1192
+ else
1193
+ Readline.completion_append_character = ""
1194
+ Readline.basic_word_break_characters = ''
1195
+ Readline.completer_word_break_characters = '' rescue nil
1196
+ Readline.completion_proc = nil
1197
+ # needs to work like $stdin.noecho
1198
+ Readline.pre_input_hook = lambda {
1199
+ Readline.output = File.open('/dev/null', 'w')
1200
+ #Readline.output = File.open(MtnCloud::Cli.windows? ? 'NUL:' : '/dev/null', 'w')
1201
+ #$stdout = File.open(MtnCloud::Cli.windows? ? 'NUL:' : '/dev/null', 'w')
1202
+ }
1203
+ password_prompt = "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+'************'+']' : ''}: "
1204
+ input = Readline.readline(password_prompt, false).to_s.chomp
1205
+ Readline.pre_input_hook = nil
1206
+ Readline.output = MtnCloud::Terminal.instance.stdout #my_terminal.stdout
1207
+ end
1208
+
1209
+ value = input
1210
+ print "\n"
1211
+ if input == '?'
1212
+ help_prompt(option_type)
1213
+ elsif input == "" && option_type['defaultValue'] != nil
1214
+ value = option_type['defaultValue'].to_s
1215
+ value_found = true
1216
+ elsif !value.empty? || option_type['required'] != true
1217
+ value_found = true
1218
+ end
1219
+ end
1220
+ return value
1221
+ end
1222
+
1223
+ def self.file_prompt(option_type)
1224
+ value_found = false
1225
+ value = nil
1226
+ while !value_found do
1227
+ #print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
1228
+ #Readline.input = $stdin
1229
+ Readline.completion_append_character = ""
1230
+ Readline.basic_word_break_characters = ''
1231
+ Readline.completer_word_break_characters = '' rescue nil
1232
+ Readline.completion_proc = proc {|s|
1233
+ # FILENAME_COMPLETION_PROC is nil in newer rubies
1234
+ Readline::FILENAME_COMPLETION_PROC.call(s) rescue []
1235
+ }
1236
+ input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: ", false).to_s
1237
+ input = input.chomp.strip
1238
+ #input = $stdin.gets.chomp!
1239
+ value = input.empty? ? option_type['defaultValue'] : input.to_s
1240
+ if input == '?'
1241
+ help_prompt(option_type)
1242
+ elsif value.empty? && option_type['required'] != true
1243
+ value = nil
1244
+ value_found = true
1245
+ elsif value
1246
+ filename = File.expand_path(value)
1247
+ if !File.exist?(filename)
1248
+ # print_red_alert "File not found: #{filename}"
1249
+ # exit 1
1250
+ print Term::ANSIColor.red," File not found: #{filename}",Term::ANSIColor.reset, "\n"
1251
+ elsif !File.file?(filename)
1252
+ print Term::ANSIColor.red," Argument is not a file: #{filename}",Term::ANSIColor.reset, "\n"
1253
+ else
1254
+ value = filename
1255
+ value_found = true
1256
+ end
1257
+ end
1258
+ end
1259
+ return value
1260
+ end
1261
+
1262
+ # file_content_prompt() prompts for source (local,repository,url) and then content or repo or.
1263
+ # returns a Hash like {sourceType:"local",content:"yadda",contentPath:null,contentRef:null}
1264
+ def self.file_content_prompt(option_type, options={}, api_client=nil, api_params={})
1265
+ file_params = {}
1266
+ options ||= {}
1267
+ full_field_key = option_type['fieldContext'] ? "#{option_type['fieldContext']}.#{option_type['fieldName']}" : "#{option_type['fieldName']}"
1268
+ passed_file_params = get_object_value(options, full_field_key)
1269
+ if passed_file_params.is_a?(Hash)
1270
+ file_params = passed_file_params
1271
+ end
1272
+ is_required = option_type['required']
1273
+
1274
+ if file_params['source']
1275
+ file_params['sourceType'] = file_params.delete('source')
1276
+ end
1277
+ source_type = file_params['sourceType']
1278
+ # source
1279
+ if source_type.nil?
1280
+ source_type = select_prompt({'fieldContext' => full_field_key, 'fieldName' => 'source', 'fieldLabel' => 'Source', 'type' => 'select', 'optionSource' => 'fileContentSource', 'required' => is_required, 'defaultValue' => (is_required ? 'local' : nil)}, api_client, {}, options[:no_prompt])
1281
+ file_params['sourceType'] = source_type
1282
+ end
1283
+ # source type options
1284
+ if source_type == "local"
1285
+ # prompt for content
1286
+ if file_params['content'].nil?
1287
+ if options[:no_prompt]
1288
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
1289
+ print Term::ANSIColor.red, " * Content [-O #{full_field_key}.content=] - File Content\n", Term::ANSIColor.reset
1290
+ print "\n"
1291
+ exit 1
1292
+ else
1293
+ file_params['content'] = multiline_prompt({'fieldContext' => full_field_key, 'fieldName' => 'content', 'type' => 'code-editor', 'fieldLabel' => 'Content', 'required' => true})
1294
+ end
1295
+ end
1296
+ elsif source_type == "url"
1297
+ if file_params['url']
1298
+ file_params['contentPath'] = file_params.delete('url')
1299
+ end
1300
+ if file_params['contentPath'].nil?
1301
+ if options[:no_prompt]
1302
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
1303
+ print Term::ANSIColor.red, " * URL [-O #{full_field_key}.url=] - Path of file in the repository\n", Term::ANSIColor.reset
1304
+ print "\n"
1305
+ exit 1
1306
+ else
1307
+ file_params['contentPath'] = generic_prompt({'fieldContext' => full_field_key, 'fieldName' => 'url', 'fieldLabel' => 'URL', 'type' => 'text', 'required' => true})
1308
+ end
1309
+ end
1310
+ elsif source_type == "repository"
1311
+ if file_params['repository'].nil?
1312
+ repository_id = select_prompt({'fieldContext' => full_field_key, 'fieldName' => 'repositoryId', 'fieldLabel' => 'Repository', 'type' => 'select', 'optionSource' => 'codeRepositories', 'required' => true}, api_client, {}, options[:no_prompt])
1313
+ file_params['repository'] = {'id' => repository_id}
1314
+ end
1315
+ if file_params['contentPath'].nil?
1316
+ if options[:no_prompt]
1317
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
1318
+ print Term::ANSIColor.red, " * File Path [-O #{full_field_key}.path=] - Path of file in the repository\n", Term::ANSIColor.reset
1319
+ print "\n"
1320
+ exit 1
1321
+ else
1322
+ file_params['contentPath'] = generic_prompt({'fieldContext' => full_field_key, 'fieldName' => 'path', 'fieldLabel' => 'File Path', 'type' => 'text', 'required' => true})
1323
+ end
1324
+ end
1325
+ if !file_params.key?('contentRef')
1326
+ if options[:no_prompt]
1327
+ # pass
1328
+ else
1329
+ file_params['contentRef'] = generic_prompt({'fieldContext' => full_field_key, 'fieldName' => 'ref', 'fieldLabel' => 'Version Ref', 'type' => 'text'})
1330
+ end
1331
+ end
1332
+ end
1333
+ return file_params
1334
+ end
1335
+
1336
+ def self.multitext_prompt(option_type)
1337
+ rtn = nil
1338
+
1339
+ # supports multi-part fields via config.fields
1340
+ # {"fields": [{"name":"tag", "required":true, "label": "Tag"}, {"name":"value", "required":false, "label": "Scope"}]}
1341
+ min_count = option_type['minCount'] || 1
1342
+ count = 0
1343
+
1344
+ if option_type['config']['fields']
1345
+ while (option_type['required'] && (rtn.empty? || count < min_count)) || self.confirm("Add#{rtn.empty? ? '': ' more'} #{option_type['fieldLabel']}?", {:default => false})
1346
+ rtn ||= []
1347
+ value = {}
1348
+ option_type['config']['fields'].each do |field|
1349
+ field_label = field['label'] || field['name'].capitalize
1350
+ value[field['name']] = generic_prompt(option_type.merge({'fieldLabel' => min_count > 1 ? "#{field_label} #{count + 1}" : field_label, 'required' => field['required'], 'description' => "#{option_type['fieldLabel']} #{field_label}"}))
1351
+ end
1352
+ rtn << value
1353
+ count += 1
1354
+ end
1355
+ else
1356
+ if rtn = generic_prompt(option_type)
1357
+ rtn = [rtn]
1358
+ while self.confirm("Add more #{option_type['fieldLabel']}?", {:default => false}) do
1359
+ rtn << generic_prompt(option_type)
1360
+ end
1361
+ end
1362
+ end
1363
+ rtn
1364
+ end
1365
+
1366
+ # file_content_prompt() prompts for source (local,repository,url) and then content or repo or.
1367
+ # returns a Hash like {sourceType:"local",content:"yadda",contentPath:null,contentRef:null}
1368
+ def self.azure_marketplace_prompt(option_type, options={}, api_client=nil, api_params={})
1369
+ cloud_id = api_params[:zoneId] || api_params[:cloudId] || api_params["zoneId"] || api_params["cloudId"]
1370
+ if cloud_id.nil?
1371
+ MtnCloud::Logging::DarkPrinter.puts "Failed to load azure marketplace offers without a zoneId" if MtnCloud::Logging.debug?
1372
+ return nil
1373
+ end
1374
+ # lets go!
1375
+ rtn = {}
1376
+ publisher_value, offer_value, sku_value, version_value = nil, nil, nil, nil
1377
+
1378
+ # Marketplace Publisher & Offer
1379
+ marketplace_api_params = {'zoneId' => cloud_id}
1380
+ v_prompt = nil
1381
+ # API endpoints moved from /api/options to /api/options/azure...
1382
+ begin
1383
+ v_prompt = prompt([{'fieldName' => 'marketplaceOffer', 'fieldLabel' => 'Azure Marketplace Offer', 'type' => 'typeahead', 'optionSourceType' => 'azure', 'optionSource' => 'searchAzureMarketplace', 'required' => true, 'description' => "Select Azure Marketplace Offer."}], options,api_client, marketplace_api_params)
1384
+ rescue => ex
1385
+ MtnCloud::Logging::DarkPrinter.puts "Failed to load azure marketplace offers, trying older endpoint" if MtnCloud::Logging.debug?
1386
+ v_prompt = prompt([{'fieldName' => 'marketplaceOffer', 'fieldLabel' => 'Azure Marketplace Offer', 'type' => 'typeahead', 'optionSource' => 'searchAzureMarketplace', 'required' => true, 'description' => "Select Azure Marketplace Offer."}], options,api_client, marketplace_api_params)
1387
+ end
1388
+ # offer_value = v_prompt['marketplaceOffer']
1389
+ # actually need both offer and publisher of these to query correctly..sigh
1390
+ marketplace_option = MtnCloud::Cli::OptionTypes.get_last_select()
1391
+ offer_value = marketplace_option['offer']
1392
+ publisher_value = marketplace_option['publisher']
1393
+
1394
+ # SKU & VERSION
1395
+ if options && options['marketplaceSku'] && options['marketplaceVersion']
1396
+ # the value to match on is actually sku|version
1397
+ options['marketplaceSku'] = options['marketplaceSku'] + '|' + options['marketplaceVersion']
1398
+ end
1399
+ sku_api_params = {'zoneId' => cloud_id, publisher: publisher_value, offer: offer_value}
1400
+ begin
1401
+ v_prompt = prompt([{'fieldName' => 'marketplaceSku', 'fieldLabel' => 'Azure Marketplace SKU', 'type' => 'select', 'optionSourceType' => 'azure', 'optionSource' => 'searchAzureMarketplaceSkus', 'required' => true, 'description' => "Select Azure Marketplace SKU and Version, the format is SKU|Version"}], options,api_client, sku_api_params)
1402
+ rescue => ex
1403
+ MtnCloud::Logging::DarkPrinter.puts "Failed to load azure marketplace offers, trying older endpoint" if MtnCloud::Logging.debug?
1404
+ v_prompt = prompt([{'fieldName' => 'marketplaceSku', 'fieldLabel' => 'Azure Marketplace SKU', 'type' => 'select', 'optionSource' => 'searchAzureMarketplaceSkus', 'required' => true, 'description' => "Select Azure Marketplace SKU and Version, the format is SKU|Version"}], options,api_client, sku_api_params)
1405
+ end
1406
+ # marketplace_option = MtnCloud::Cli::OptionTypes.get_last_select()
1407
+ # sku_value = marketplace_option['sku']
1408
+ # version_value = marketplace_option['version']
1409
+ sku_value = v_prompt['marketplaceSku']
1410
+ if sku_value && sku_value.include?("|")
1411
+ sku_value, version_value = sku_value.split("|")
1412
+ end
1413
+ # rtn['publisher'] = publisher_value
1414
+ # rtn['offer'] = offer_value
1415
+ # rtn['sku'] = sku_value
1416
+ # rtn['version'] = version_value
1417
+ # return rtn
1418
+ # instance provisioning expects these parameters...
1419
+ return {'marketplacePublisher' => publisher_value, 'marketplaceOffer' => offer_value, 'marketplaceSku' => sku_value, 'marketplaceVersion' => version_value}
1420
+ end
1421
+
1422
+ def self.load_options(option_type, api_client, api_params, query_value=nil)
1423
+ field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
1424
+ help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
1425
+ select_options = []
1426
+ # local array of options
1427
+ if option_type['selectOptions']
1428
+ # calculate from inline lambda
1429
+ if option_type['selectOptions'].is_a?(Proc)
1430
+ select_options = option_type['selectOptions'].call(api_client, api_params || {})
1431
+ else
1432
+ select_options = option_type['selectOptions']
1433
+ end
1434
+ # filter options ourselves
1435
+ if query_value.to_s != ""
1436
+ filtered_options = select_options.select { |it| it['value'].to_s == query_value.to_s }
1437
+ if filtered_options.empty?
1438
+ filtered_options = select_options.select { |it| it['name'].to_s == query_value.to_s }
1439
+ end
1440
+ select_options = filtered_options
1441
+ end
1442
+ elsif option_type['optionSource']
1443
+ api_params = api_params.select {|k,v| option_type['params'].include(k)} if !option_type['params'].nil? && api_params
1444
+
1445
+ # calculate from inline lambda
1446
+ if option_type['optionSource'].is_a?(Proc)
1447
+ select_options = option_type['optionSource'].call(api_client, api_params || {})
1448
+ elsif option_type['optionSource'] == 'list'
1449
+ # /api/options/list is a special action for custom OptionTypeLists, just need to pass the optionTypeId parameter
1450
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, (api_params || {}).merge({'optionTypeId' => option_type['id']}))
1451
+ else
1452
+ # remote optionSource aka /api/options/$optionSource?
1453
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
1454
+ end
1455
+ else
1456
+ raise "option '#{help_field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
1457
+ end
1458
+
1459
+ return select_options
1460
+ end
1461
+
1462
+ def self.help_prompt(option_type)
1463
+ field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
1464
+ help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
1465
+ # an attempt at prompting help for natural options without the -O switch
1466
+ if option_type[:fmt] == :natural
1467
+ print Term::ANSIColor.green," * #{option_type['fieldLabel']} [--#{help_field_key}=] ", Term::ANSIColor.reset , "#{option_type['description']}\n"
1468
+ else
1469
+ print Term::ANSIColor.green," * #{option_type['fieldLabel']} [-O #{help_field_key}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
1470
+ end
1471
+ if option_type['type'].to_s == 'typeahead'
1472
+ print "This is a typeahead input. Enter the name or value of an option.\n"
1473
+ print "If the specified input matches more than one option, they will be printed and you will be prompted again.\n"
1474
+ print "the matching options will be shown and you can try again.\n"
1475
+ end
1476
+ end
1477
+
1478
+
1479
+ def self.load_source_options(source,sourceType,api_client,params)
1480
+ api_client.options.options_for_source("#{sourceType ? "#{sourceType}/" : ''}#{source}", params)['data']
1481
+ end
1482
+
1483
+ def self.format_select_options_help(opt, select_options = [], paging = nil)
1484
+ out = ""
1485
+ header = opt['fieldLabel'] ? "#{opt['fieldLabel']} Options" : "Options"
1486
+ value_field = (opt['config'] ? opt['config']['valueField'] : nil) || 'value'
1487
+ if paging
1488
+ offset = paging[:cur_page] * paging[:page_size]
1489
+ limit = [offset + paging[:page_size], select_options.count].min - 1
1490
+ header = "#{header} (#{offset+1}-#{limit+1} of #{paging[:total]})"
1491
+ select_options = select_options[(offset)..(limit)]
1492
+ end
1493
+ out = ""
1494
+ out << "\n"
1495
+ out << "#{header}\n"
1496
+ out << "#{'=' * header.length}\n"
1497
+ select_options.each do |option|
1498
+ out << (option['isGroup'] ? "- #{option['name']}\n" : " * #{option['name']} [#{option[value_field]}]\n")
1499
+ end
1500
+ return out
1501
+ end
1502
+
1503
+ def self.sort_option_types(option_types)
1504
+ sorted = if option_types.find {|it| it['fieldGroup'] || it['displayOrder'] }
1505
+ option_types.select {|it| (it['fieldGroup'] || 'default').casecmp?('default')}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i} +
1506
+ option_types.reject {|it| ['default', 'advanced'].include?((it['fieldGroup'] || 'default').downcase)}.sort{|a,b| a['displayOrder'] <=> b['displayOrder']}.group_by{|it| it['fieldGroup']}.values.collect { |it| it.sort{|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}}.flatten +
1507
+ option_types.select {|it| 'advanced'.casecmp?(it['fieldGroup'])}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}
1508
+ else
1509
+ option_types
1510
+ end
1511
+ reorder_option_type_dependencies(sorted)
1512
+ end
1513
+
1514
+ # displayOrder alone can put a field ahead of another field it depends on
1515
+ # via dependsOnCode/visibleOnCode (eg. a hidden, optionSource-populated field
1516
+ # with a high displayOrder that a lower displayOrder field is gated on). Move
1517
+ # any depended-upon option type ahead of the option type(s) that depend on it,
1518
+ # preserving displayOrder order otherwise.
1519
+ def self.reorder_option_type_dependencies(option_types)
1520
+ field_key_for = lambda {|it| it['fieldContext'].to_s.empty? ? it['fieldName'].to_s : "#{it['fieldContext']}.#{it['fieldName']}" }
1521
+ depends_on_codes_for = lambda {|option_type|
1522
+ depends_value = option_type['visibleOnCode'].to_s.empty? ? option_type['dependsOnCode'] : option_type['visibleOnCode']
1523
+ next [] if depends_value.to_s.empty? || depends_value == 'networkInterfaces'
1524
+ depends_value = depends_value[(depends_value.index('::') + 2)..-1] if depends_value.include?('::')
1525
+ depends_value.to_s.sub(',', ' ').split(' ').collect {|value| value.split(':')[0] }
1526
+ }
1527
+ result = option_types.dup
1528
+ passes = 0
1529
+ changed = true
1530
+ while changed && passes < result.size
1531
+ changed = false
1532
+ passes += 1
1533
+ result.each_with_index do |option_type, i|
1534
+ depends_on_codes_for.call(option_type).each do |depends_on_code|
1535
+ dep_index = result.index {|it| it['code'] == depends_on_code || field_key_for.call(it) == depends_on_code }
1536
+ if dep_index && dep_index > i
1537
+ result.insert(i, result.delete_at(dep_index))
1538
+ changed = true
1539
+ break
1540
+ end
1541
+ end
1542
+ break if changed
1543
+ end
1544
+ end
1545
+ result
1546
+ end
1547
+
1548
+ def self.display_select_options(opt, select_options = [], paging = nil)
1549
+ puts self.format_select_options_help(opt, select_options, paging)
1550
+ end
1551
+
1552
+ def self.format_option_types_help(option_types, opts={})
1553
+ option_types = self.sort_option_types(option_types).reject {|it| it['hidden']}
1554
+
1555
+ if option_types.empty?
1556
+ "#{opts[:color]}#{opts[:title] || "Available Options:"}\nNone\n\n"
1557
+ else
1558
+ if opts[:include_context]
1559
+ option_lines = option_types.collect {|it|
1560
+ field_context = (opts[:context_map] || {})[it['fieldContext']] || it['fieldContext']
1561
+ " -O #{field_context && field_context != '' ? "#{field_context}." : ''}#{it['fieldName']}=\"value\""
1562
+ }
1563
+ else
1564
+ option_lines = option_types.collect {|it| " -O #{it['fieldName']}=\"value\"" }
1565
+ end
1566
+ "#{opts[:color]}#{opts[:title] || "Available Options:"}\n#{option_lines.join("\n")}\n\n"
1567
+ end
1568
+ end
1569
+
1570
+ def self.display_option_types_help(option_types, opts={})
1571
+ puts self.format_option_types_help(option_types, opts)
1572
+ end
1573
+
1574
+ def self.optional_label(option_type)
1575
+ # removing this for now, for the sake of providing less to look at
1576
+ if option_type[:fmt] == :natural # || true
1577
+ return ""
1578
+ else
1579
+ return option_type['required'] ? '' : ' (optional)'
1580
+ end
1581
+ end
1582
+
1583
+ def self.get_option_value(obj, option_type, format=false)
1584
+ context = option_type['fieldContext'] == 'config' ? obj['config'] : obj
1585
+ name = option_type['fieldName']
1586
+ tokens = name.split('.')
1587
+
1588
+ if tokens.length > 1
1589
+ tokens.slice(0, tokens.length - 1).each do |token|
1590
+ context = context[token]
1591
+ end
1592
+ name = tokens.last
1593
+ end
1594
+
1595
+ if context.kind_of?(Array)
1596
+ rtn = context.collect {|it| it['name'] || it[name]}.join ', '
1597
+ else
1598
+ rtn = context[name]
1599
+ end
1600
+
1601
+ if format
1602
+ rtn = (rtn ? 'On' : 'Off') if option_type['type'] == 'checkbox'
1603
+ rtn = rtn.join(', ') if rtn.is_a?(Array)
1604
+ end
1605
+ rtn
1606
+ end
1607
+
1608
+ def self.try_as_json(val)
1609
+ if val.is_a?(String)
1610
+ if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
1611
+ begin
1612
+ val = JSON.parse(val)
1613
+ rescue
1614
+ MtnCloud::Logging::DarkPrinter.puts "Failed to parse option value '#{val}' as JSON" if MtnCloud::Logging.debug?
1615
+ end
1616
+ end
1617
+ end
1618
+ return val
1619
+ end
1620
+ end
1621
+ end
1622
+ end