@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5

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 (315) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/styles/global/_layout.scss +4 -0
  3. package/assets/translations/en-us.yaml +183 -51
  4. package/assets/translations/zh-hans.yaml +1 -7
  5. package/chart/monitoring/ClusterSelector.vue +0 -21
  6. package/chart/monitoring/prometheus/index.vue +6 -3
  7. package/components/ActionDropdownShell.vue +5 -3
  8. package/components/ButtonGroup.vue +26 -1
  9. package/components/CruResource.vue +212 -16
  10. package/components/ExplorerMembers.vue +8 -4
  11. package/components/ExplorerProjectsNamespaces.vue +10 -6
  12. package/components/GrowlManager.vue +4 -0
  13. package/components/MgmtNodeList.vue +184 -0
  14. package/components/PromptRestore.vue +93 -32
  15. package/components/Questions/index.vue +1 -0
  16. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  17. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  18. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  19. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  21. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  22. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  23. package/components/ResourceDetail/index.vue +1 -1
  24. package/components/ResourceList/Masthead.vue +7 -1
  25. package/components/ResourceList/index.vue +82 -1
  26. package/components/ResourceTable.vue +1 -0
  27. package/components/RichTranslation.vue +5 -2
  28. package/components/Setting.vue +1 -0
  29. package/components/SortableTable/index.vue +4 -3
  30. package/components/SubtleLink.vue +31 -6
  31. package/components/Tabbed/Tab.vue +29 -3
  32. package/components/Tabbed/index.vue +25 -3
  33. package/components/TableOfContents/TableOfContents.vue +109 -0
  34. package/components/TableOfContents/composables.ts +258 -0
  35. package/components/Window/ContainerShell.vue +21 -11
  36. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  37. package/components/Wizard.vue +23 -5
  38. package/components/__tests__/ButtonGroup.test.ts +56 -0
  39. package/components/__tests__/PromptRestore.test.ts +169 -19
  40. package/components/fleet/AppCoChartGrid.vue +401 -0
  41. package/components/fleet/AppCoEmptyState.vue +127 -0
  42. package/components/fleet/AppCoPageHeader.vue +119 -0
  43. package/components/fleet/AppCoVersionSelect.vue +70 -0
  44. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  45. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  46. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  47. package/components/fleet/FleetIntro.vue +7 -3
  48. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  49. package/components/fleet/FleetSecretSelector.vue +5 -3
  50. package/components/fleet/FleetValuesFrom.vue +8 -2
  51. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  52. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  53. package/components/fleet/GitRepoTargetTab.vue +0 -2
  54. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  55. package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
  56. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  57. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  58. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  59. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  60. package/components/fleet/HelmOpTargetTab.vue +64 -60
  61. package/components/fleet/HelmOpValuesTab.vue +129 -105
  62. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  63. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  64. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  65. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  66. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  67. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  68. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  69. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  70. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  71. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  72. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  73. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  74. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  75. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  76. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  77. package/components/fleet/dashboard/Empty.vue +8 -4
  78. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  79. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  80. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  81. package/components/form/ArrayList.vue +61 -4
  82. package/components/form/FileSelector.vue +39 -1
  83. package/components/form/KeyValue.vue +23 -2
  84. package/components/form/LabeledSelect.vue +39 -1
  85. package/components/form/Labels.vue +22 -3
  86. package/components/form/NameNsDescription.vue +13 -5
  87. package/components/form/PrivateRegistry.constants.ts +7 -0
  88. package/components/form/PrivateRegistry.vue +253 -18
  89. package/components/form/ResourceTabs/index.vue +1 -0
  90. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  91. package/components/form/__tests__/FileSelector.test.ts +23 -0
  92. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  93. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  94. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  95. package/components/formatter/EtcdSnapshotName.vue +73 -0
  96. package/components/formatter/InternalExternalIP.vue +10 -4
  97. package/components/formatter/ServiceTargets.vue +26 -7
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  99. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  100. package/components/nav/Header.vue +12 -1
  101. package/components/nav/TopLevelMenu.vue +7 -2
  102. package/components/nav/__tests__/Header.test.ts +15 -0
  103. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  104. package/components/templates/default.vue +16 -4
  105. package/components/templates/home.vue +9 -4
  106. package/components/templates/plain.vue +9 -4
  107. package/composables/useHelmOpResources.test.ts +56 -0
  108. package/composables/useHelmOpResources.ts +32 -0
  109. package/composables/useStateColor.test.ts +325 -0
  110. package/composables/useStateColor.ts +128 -0
  111. package/config/features.js +1 -0
  112. package/config/home-links.js +1 -1
  113. package/config/labels-annotations.js +3 -0
  114. package/config/product/explorer.js +17 -4
  115. package/config/product/manager.js +8 -0
  116. package/config/router/index.js +16 -0
  117. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  118. package/config/router/navigation-guards/authentication.js +10 -4
  119. package/config/router/routes.js +20 -6
  120. package/config/secret.ts +10 -0
  121. package/config/settings.ts +6 -4
  122. package/config/table-headers.js +3 -4
  123. package/config/types.js +16 -0
  124. package/core/plugin-products-base.ts +3 -3
  125. package/core/plugin-types.ts +83 -30
  126. package/core/plugin.ts +3 -0
  127. package/core/types-provisioning.ts +34 -1
  128. package/core/types.ts +15 -2
  129. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  130. package/detail/__tests__/workload.test.ts +3 -152
  131. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  132. package/detail/provisioning.cattle.io.cluster.vue +109 -7
  133. package/detail/workload/index.vue +12 -55
  134. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  135. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  136. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  137. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  138. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
  139. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  140. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  141. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  142. package/edit/auth/__tests__/azuread.test.ts +34 -9
  143. package/edit/auth/__tests__/github.test.ts +234 -0
  144. package/edit/auth/__tests__/oidc.test.ts +26 -6
  145. package/edit/auth/__tests__/saml.test.ts +196 -0
  146. package/edit/auth/azuread.vue +128 -95
  147. package/edit/auth/github.vue +72 -13
  148. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  149. package/edit/auth/ldap/config.vue +8 -0
  150. package/edit/auth/ldap/index.vue +75 -1
  151. package/edit/auth/oidc.vue +119 -73
  152. package/edit/auth/saml.vue +76 -12
  153. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  154. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  155. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  156. package/edit/fleet.cattle.io.helmop.vue +542 -141
  157. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  158. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  159. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  160. package/edit/management.cattle.io.user.vue +5 -2
  161. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  162. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  163. package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
  164. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  165. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  166. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  167. package/list/group.principal.vue +5 -4
  168. package/list/harvesterhci.io.management.cluster.vue +8 -9
  169. package/list/management.cattle.io.user.vue +12 -9
  170. package/list/provisioning.cattle.io.cluster.vue +16 -10
  171. package/mixins/__tests__/auth-config.test.ts +90 -0
  172. package/mixins/__tests__/chart.test.ts +94 -0
  173. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  174. package/mixins/auth-config.js +7 -0
  175. package/mixins/chart.js +11 -2
  176. package/mixins/child-hook.js +12 -6
  177. package/mixins/create-edit-view/impl.js +5 -3
  178. package/mixins/resource-fetch-api-pagination.js +21 -1
  179. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  180. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  181. package/models/__tests__/fleet-application.test.ts +175 -0
  182. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  183. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  184. package/models/__tests__/management.cattle.io.node.ts +22 -0
  185. package/models/__tests__/namespace.test.ts +36 -0
  186. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
  187. package/models/__tests__/secret.test.ts +68 -1
  188. package/models/__tests__/workload.test.ts +401 -26
  189. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  190. package/models/compliance.cattle.io.clusterscan.js +39 -4
  191. package/models/fleet-application.js +4 -0
  192. package/models/fleet.cattle.io.helmop.js +20 -1
  193. package/models/management.cattle.io.cluster.js +39 -5
  194. package/models/management.cattle.io.node.js +44 -3
  195. package/models/namespace.js +1 -1
  196. package/models/pod.js +46 -3
  197. package/models/provisioning.cattle.io.cluster.js +64 -14
  198. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  199. package/models/secret.js +19 -0
  200. package/models/workload.js +120 -20
  201. package/models/workload.service.js +5 -0
  202. package/package.json +14 -13
  203. package/pages/about.vue +5 -6
  204. package/pages/auth/login.vue +0 -35
  205. package/pages/auth/setup.vue +11 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  208. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  209. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  210. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  211. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  212. package/pages/c/_cluster/apps/charts/install.vue +236 -144
  213. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  214. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  215. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  216. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  217. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  218. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  219. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  220. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  221. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  222. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  223. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  224. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  225. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  226. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  227. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  228. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  229. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  230. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  231. package/pages/c/_cluster/fleet/index.vue +2 -2
  232. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  233. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  234. package/pages/fail-whale.vue +16 -11
  235. package/pages/home.vue +16 -46
  236. package/pkg/require-asset.lib.js +25 -0
  237. package/pkg/vue.config.js +7 -0
  238. package/plugins/clean-html.d.ts +9 -0
  239. package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
  240. package/plugins/dashboard-store/getters.js +0 -1
  241. package/plugins/dashboard-store/resource-class.js +114 -19
  242. package/plugins/steve/__tests__/actions.test.ts +212 -0
  243. package/plugins/steve/actions.js +96 -0
  244. package/plugins/steve/steve-pagination-utils.ts +1 -1
  245. package/rancher-components/Accordion/Accordion.vue +53 -9
  246. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  247. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  248. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  249. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  250. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  251. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  252. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  253. package/rancher-components/RcButton/RcButton.vue +94 -15
  254. package/rancher-components/RcButton/index.ts +1 -1
  255. package/rancher-components/RcButton/types.ts +3 -0
  256. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  257. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  258. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  259. package/rancher-components/RcSection/RcSection.vue +28 -3
  260. package/scripts/extension/helm/package/Dockerfile +1 -1
  261. package/scripts/test-plugins-build.sh +2 -1
  262. package/store/__tests__/features.test.ts +131 -0
  263. package/store/__tests__/growl.test.ts +374 -0
  264. package/store/__tests__/modal.test.ts +131 -0
  265. package/store/__tests__/notifications.test.ts +434 -0
  266. package/store/__tests__/slideInPanel.test.ts +88 -0
  267. package/store/__tests__/type-map.utils.test.ts +433 -0
  268. package/store/catalog.js +57 -0
  269. package/store/features.js +4 -0
  270. package/store/plugins.js +7 -4
  271. package/types/components/buttonGroup.ts +5 -0
  272. package/types/shell/index.d.ts +166 -70
  273. package/utils/__tests__/auth.test.ts +273 -0
  274. package/utils/__tests__/computed.test.ts +193 -0
  275. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  276. package/utils/__tests__/dom.test.ts +81 -0
  277. package/utils/__tests__/duration.test.ts +37 -1
  278. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  279. package/utils/__tests__/fleet-appco.test.ts +312 -0
  280. package/utils/__tests__/monitoring.test.ts +130 -0
  281. package/utils/__tests__/object.test.ts +22 -0
  282. package/utils/__tests__/operation-cr.test.ts +34 -0
  283. package/utils/__tests__/platform.test.ts +91 -0
  284. package/utils/__tests__/position.test.ts +237 -0
  285. package/utils/__tests__/provider.test.ts +51 -1
  286. package/utils/__tests__/queue.test.ts +232 -0
  287. package/utils/__tests__/release-notes.test.ts +221 -0
  288. package/utils/__tests__/router.test.js +254 -1
  289. package/utils/__tests__/select.test.ts +208 -0
  290. package/utils/__tests__/time.test.ts +265 -1
  291. package/utils/__tests__/title.test.ts +47 -0
  292. package/utils/__tests__/width.test.ts +53 -0
  293. package/utils/__tests__/window.test.ts +158 -0
  294. package/utils/__tests__/xccdf.test.ts +126 -6
  295. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  296. package/utils/crypto/__tests__/index.test.ts +144 -0
  297. package/utils/duration.ts +104 -0
  298. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  299. package/utils/dynamic-content/info.ts +2 -1
  300. package/utils/error.js +13 -0
  301. package/utils/fleet-appco.ts +323 -0
  302. package/utils/object.js +22 -2
  303. package/utils/operation-cr.js +19 -0
  304. package/utils/provider.ts +12 -0
  305. package/utils/require-asset.ts +7 -0
  306. package/utils/validators/__tests__/container-images.test.ts +104 -0
  307. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  308. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  309. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  310. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  311. package/utils/validators/private-registry.ts +15 -4
  312. package/utils/xccdf.ts +39 -42
  313. package/vue.config.js +1 -1
  314. package/pages/support/index.vue +0 -264
  315. package/utils/duration.js +0 -43
@@ -0,0 +1,433 @@
1
+ import {
2
+ conditionalDepaginate,
3
+ configureConditionalDepaginate,
4
+ headerFromSchemaCol,
5
+ rowValueGetter,
6
+ } from '../type-map.utils';
7
+
8
+ const makeGetters = (overrides: any = {}) => ({
9
+ 'i18n/exists': jest.fn(() => false),
10
+ 'i18n/t': jest.fn((key: string) => key),
11
+ currentStore: jest.fn(() => 'cluster'),
12
+ 'cluster/all': jest.fn(() => []),
13
+ ...overrides,
14
+ });
15
+
16
+ const makeCol = (overrides: any = {}) => ({
17
+ description: '',
18
+ field: '$.metadata.name',
19
+ format: '',
20
+ name: 'Name',
21
+ priority: 0,
22
+ type: 'string',
23
+ ...overrides,
24
+ });
25
+
26
+ const makeAgeColumn = () => ({
27
+ name: 'age',
28
+ label: 'Age',
29
+ value: 'metadata.creationTimestamp',
30
+ sort: ['metadata.creationTimestamp'],
31
+ });
32
+
33
+ describe('rowValueGetter', () => {
34
+ describe('fields matching $.metadata.fields[N] pattern', () => {
35
+ it.each([
36
+ {
37
+ desc: 'returns function accessing index 0',
38
+ field: '$.metadata.fields[0]',
39
+ asFn: true as const,
40
+ expectedValue: { metadata: { fields: ['first', 'second'] } },
41
+ expectedIdx: 0,
42
+ },
43
+ {
44
+ desc: 'returns function accessing index 2',
45
+ field: '$.metadata.fields[2]',
46
+ asFn: true as const,
47
+ expectedValue: { metadata: { fields: ['a', 'b', 'c'] } },
48
+ expectedIdx: 2,
49
+ },
50
+ ])('$desc', ({
51
+ field, asFn, expectedValue, expectedIdx,
52
+ }) => {
53
+ const getter = rowValueGetter(makeCol({ field }), asFn) as (row: any) => any;
54
+
55
+ expect(typeof getter).toStrictEqual('function');
56
+ expect(getter(expectedValue)).toStrictEqual(expectedValue.metadata.fields[expectedIdx]);
57
+ });
58
+
59
+ it.each([
60
+ {
61
+ desc: 'returns string path for index 0',
62
+ field: '$.metadata.fields[0]',
63
+ asFn: false as const,
64
+ expected: 'metadata.fields.0',
65
+ },
66
+ {
67
+ desc: 'returns string path for index 3',
68
+ field: '$.metadata.fields[3]',
69
+ asFn: false as const,
70
+ expected: 'metadata.fields.3',
71
+ },
72
+ ])('$desc', ({ field, asFn, expected }) => {
73
+ expect(rowValueGetter(makeCol({ field }), asFn)).toStrictEqual(expected);
74
+ });
75
+
76
+ it('returns undefined when row has no fields array', () => {
77
+ const getter = rowValueGetter(makeCol({ field: '$.metadata.fields[0]' }), true) as (row: any) => any;
78
+
79
+ expect(getter({})).toBeUndefined();
80
+ });
81
+
82
+ it('returns undefined when metadata is missing', () => {
83
+ const getter = rowValueGetter(makeCol({ field: '$.metadata.fields[1]' }), true) as (row: any) => any;
84
+
85
+ expect(getter({ other: 'data' })).toBeUndefined();
86
+ });
87
+ });
88
+
89
+ describe('fields starting with . (dot-prefixed)', () => {
90
+ it('prepends $ to dot-prefixed fields', () => {
91
+ const result = rowValueGetter(makeCol({ field: '.metadata.name' }), false);
92
+
93
+ expect(result).toStrictEqual('$.metadata.name');
94
+ });
95
+
96
+ it('does not modify fields already starting with $', () => {
97
+ const result = rowValueGetter(makeCol({ field: '$.metadata.name' }), false);
98
+
99
+ expect(result).toStrictEqual('$.metadata.name');
100
+ });
101
+ });
102
+
103
+ describe('fields with escaped dots (rewriteJsonPath)', () => {
104
+ it.each([
105
+ {
106
+ desc: 'rewrites single escaped dot in label key',
107
+ field: '$.metadata.labels.topology\\.kubernetes\\.io/zone',
108
+ expected: '$.metadata.labels.["topology.kubernetes.io/zone"]',
109
+ },
110
+ {
111
+ desc: 'rewrites escaped dot in annotation key',
112
+ field: '$.metadata.annotations.cattle\\.io/hash',
113
+ expected: '$.metadata.annotations.["cattle.io/hash"]',
114
+ },
115
+ ])('$desc', ({ field, expected }) => {
116
+ expect(rowValueGetter(makeCol({ field }), false)).toStrictEqual(expected);
117
+ });
118
+
119
+ it('returns path unchanged when no escaped dots present', () => {
120
+ const field = '$.metadata.labels.app';
121
+
122
+ expect(rowValueGetter(makeCol({ field }), false)).toStrictEqual(field);
123
+ });
124
+ });
125
+ });
126
+
127
+ describe('conditionalDepaginate', () => {
128
+ it.each([
129
+ {
130
+ desc: 'returns true when depaginate is boolean true',
131
+ depaginate: true,
132
+ args: undefined,
133
+ expected: true,
134
+ },
135
+ {
136
+ desc: 'returns false when depaginate is boolean false',
137
+ depaginate: false,
138
+ args: undefined,
139
+ expected: false,
140
+ },
141
+ {
142
+ desc: 'returns undefined when depaginate is undefined',
143
+ depaginate: undefined,
144
+ args: undefined,
145
+ expected: undefined,
146
+ },
147
+ ])('$desc', ({ depaginate, args, expected }) => {
148
+ expect(conditionalDepaginate(depaginate as any, args)).toStrictEqual(expected);
149
+ });
150
+
151
+ it('calls function with args and returns its result when args are provided', () => {
152
+ const fn = jest.fn(() => true);
153
+ const args = { ctx: { rootGetters: {} as any }, args: { type: 'pod', opt: {} } };
154
+
155
+ const result = conditionalDepaginate(fn, args);
156
+
157
+ expect(fn).toHaveBeenCalledWith(args);
158
+ expect(result).toStrictEqual(true);
159
+ });
160
+
161
+ it('returns false when depaginate is a function but no args are provided', () => {
162
+ const fn = jest.fn(() => true);
163
+
164
+ expect(conditionalDepaginate(fn, undefined)).toStrictEqual(false);
165
+ expect(fn).not.toHaveBeenCalled();
166
+ });
167
+ });
168
+
169
+ describe('configureConditionalDepaginate', () => {
170
+ const makeRootGetters = (resourceCount: number | undefined, type: string, store = 'cluster') => {
171
+ const counts: any = {};
172
+
173
+ if (resourceCount !== undefined) {
174
+ counts[type] = { summary: { count: resourceCount } };
175
+ }
176
+
177
+ return {
178
+ currentStore: jest.fn(() => store),
179
+ [`${ store }/all`]: jest.fn(() => [{ counts }]),
180
+ };
181
+ };
182
+
183
+ it('returns true when resource count is below maxResourceCount', () => {
184
+ const type = 'pod';
185
+ const rootGetters = makeRootGetters(50, type) as any;
186
+ const fn = configureConditionalDepaginate({ maxResourceCount: 100, isNorman: false });
187
+ const result = fn({
188
+ ctx: { rootGetters },
189
+ args: { type, opt: {} },
190
+ });
191
+
192
+ expect(result).toStrictEqual(true);
193
+ });
194
+
195
+ it('returns false when resource count equals maxResourceCount', () => {
196
+ const type = 'pod';
197
+ const rootGetters = makeRootGetters(100, type) as any;
198
+ const fn = configureConditionalDepaginate({ maxResourceCount: 100, isNorman: false });
199
+ const result = fn({
200
+ ctx: { rootGetters },
201
+ args: { type, opt: {} },
202
+ });
203
+
204
+ expect(result).toStrictEqual(false);
205
+ });
206
+
207
+ it('returns false when resource count exceeds maxResourceCount', () => {
208
+ const type = 'pod';
209
+ const rootGetters = makeRootGetters(200, type) as any;
210
+ const fn = configureConditionalDepaginate({ maxResourceCount: 100, isNorman: false });
211
+ const result = fn({
212
+ ctx: { rootGetters },
213
+ args: { type, opt: {} },
214
+ });
215
+
216
+ expect(result).toStrictEqual(false);
217
+ });
218
+
219
+ it('returns false when resource count is undefined', () => {
220
+ const type = 'pod';
221
+ const rootGetters = makeRootGetters(undefined, type) as any;
222
+ const fn = configureConditionalDepaginate({ maxResourceCount: 100, isNorman: false });
223
+ const result = fn({
224
+ ctx: { rootGetters },
225
+ args: { type, opt: {} },
226
+ });
227
+
228
+ expect(result).toStrictEqual(false);
229
+ });
230
+
231
+ it('uses management.cattle.io. prefix for Norman types', () => {
232
+ const type = 'node';
233
+ const normanType = `management.cattle.io.${ type }`;
234
+ const rootGetters = makeRootGetters(5, normanType) as any;
235
+ const fn = configureConditionalDepaginate({ maxResourceCount: 100, isNorman: true });
236
+ const result = fn({
237
+ ctx: { rootGetters },
238
+ args: { type, opt: {} },
239
+ });
240
+
241
+ expect(result).toStrictEqual(true);
242
+ });
243
+ });
244
+
245
+ describe('headerFromSchemaCol', () => {
246
+ describe('age column shortcut', () => {
247
+ it.each([
248
+ {
249
+ desc: 'returns ageColumn when format is empty and name is age',
250
+ col: makeCol({
251
+ name: 'age',
252
+ format: '',
253
+ }),
254
+ },
255
+ {
256
+ desc: 'returns ageColumn when format is date and name is age',
257
+ col: makeCol({
258
+ name: 'age',
259
+ format: 'date',
260
+ }),
261
+ },
262
+ {
263
+ desc: 'returns ageColumn when type is date and name is age',
264
+ col: makeCol({
265
+ name: 'age',
266
+ type: 'date',
267
+ format: '',
268
+ }),
269
+ },
270
+ {
271
+ desc: 'returns ageColumn when name is Age (case insensitive)',
272
+ col: makeCol({
273
+ name: 'Age',
274
+ format: '',
275
+ }),
276
+ },
277
+ ])('$desc', ({ col }) => {
278
+ const ageColumn = makeAgeColumn() as any;
279
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, ageColumn);
280
+
281
+ expect(result).toStrictEqual(ageColumn);
282
+ });
283
+
284
+ it('does not return ageColumn when ageColumn is not provided', () => {
285
+ const col = makeCol({ name: 'age', format: '' });
286
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
287
+
288
+ expect(result).not.toStrictEqual(null);
289
+ expect(result.name).toStrictEqual('age');
290
+ });
291
+ });
292
+
293
+ describe('formatter assignment', () => {
294
+ it('sets formatter to Date and width 120 for date format', () => {
295
+ const col = makeCol({
296
+ name: 'created',
297
+ format: 'date',
298
+ });
299
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
300
+
301
+ expect(result.formatter).toStrictEqual('Date');
302
+ expect(result.width).toStrictEqual(120);
303
+ expect(result.formatterOpts).toStrictEqual({ multiline: true });
304
+ });
305
+
306
+ it('sets formatter to Date and width 120 when type is date', () => {
307
+ const col = makeCol({
308
+ name: 'modified',
309
+ format: '',
310
+ type: 'date',
311
+ });
312
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
313
+
314
+ expect(result.formatter).toStrictEqual('Date');
315
+ expect(result.width).toStrictEqual(120);
316
+ });
317
+
318
+ it('sets formatter to Number when type is number', () => {
319
+ const col = makeCol({
320
+ name: 'count',
321
+ type: 'number',
322
+ });
323
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
324
+
325
+ expect(result.formatter).toStrictEqual('Number');
326
+ });
327
+
328
+ it('sets formatter to Number when type is int', () => {
329
+ const col = makeCol({
330
+ name: 'replicas',
331
+ type: 'int',
332
+ });
333
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
334
+
335
+ expect(result.formatter).toStrictEqual('Number');
336
+ });
337
+
338
+ it('does not set formatter for plain text column', () => {
339
+ const col = makeCol({ name: 'status', type: 'string' });
340
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
341
+
342
+ expect(result.formatter).toBeUndefined();
343
+ });
344
+ });
345
+
346
+ describe('label resolution', () => {
347
+ it('uses i18n translation when key exists', () => {
348
+ const getters = makeGetters({
349
+ 'i18n/exists': jest.fn(() => true),
350
+ 'i18n/t': jest.fn((key: string) => `translated:${ key }`),
351
+ });
352
+ const col = makeCol({ name: 'status' });
353
+ const result = headerFromSchemaCol(col as any, getters as any, false, null as any);
354
+
355
+ expect(result.label).toStrictEqual('translated:tableHeaders.status');
356
+ });
357
+
358
+ it('uses column name when i18n key does not exist', () => {
359
+ const getters = makeGetters({ 'i18n/exists': jest.fn(() => false) });
360
+ const col = makeCol({ name: 'MyColumn' });
361
+ const result = headerFromSchemaCol(col as any, getters as any, false, null as any);
362
+
363
+ expect(result.label).toStrictEqual('MyColumn');
364
+ });
365
+
366
+ it('camelCases the i18n key when col name has spaces', () => {
367
+ const getters = makeGetters({
368
+ 'i18n/exists': jest.fn((key: string) => key === 'tableHeaders.myLabel'),
369
+ 'i18n/t': jest.fn((key: string) => `t:${ key }`),
370
+ });
371
+ const col = makeCol({ name: 'my label' });
372
+ const result = headerFromSchemaCol(col as any, getters as any, false, null as any);
373
+
374
+ expect(result.label).toStrictEqual('t:tableHeaders.myLabel');
375
+ });
376
+ });
377
+
378
+ describe('tooltip from description', () => {
379
+ it('uses description as tooltip when not ending with dot', () => {
380
+ const col = makeCol({ name: 'status', description: 'Current status' });
381
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
382
+
383
+ expect(result.tooltip).toStrictEqual('Current status');
384
+ });
385
+
386
+ it('strips trailing dot from description for tooltip', () => {
387
+ const col = makeCol({ name: 'status', description: 'Current status.' });
388
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
389
+
390
+ expect(result.tooltip).toStrictEqual('Current status');
391
+ });
392
+
393
+ it('uses empty string as tooltip when description is empty', () => {
394
+ const col = makeCol({ name: 'status', description: '' });
395
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
396
+
397
+ expect(result.tooltip).toStrictEqual('');
398
+ });
399
+ });
400
+
401
+ describe('pagination mode', () => {
402
+ it('uses string path as value when pagination is true', () => {
403
+ const col = makeCol({ name: 'status', field: '$.metadata.name' });
404
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, true, null as any);
405
+
406
+ expect(typeof result.value).toStrictEqual('string');
407
+ });
408
+
409
+ it('uses function as value when pagination is false', () => {
410
+ const col = makeCol({ name: 'status', field: '$.metadata.fields[0]' });
411
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
412
+
413
+ expect(typeof result.value).toStrictEqual('function');
414
+ });
415
+ });
416
+
417
+ describe('output shape', () => {
418
+ it('sets name to lowercase version of col.name', () => {
419
+ const col = makeCol({ name: 'Status' });
420
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, false, null as any);
421
+
422
+ expect(result.name).toStrictEqual('status');
423
+ });
424
+
425
+ it('sets sort and search from string path', () => {
426
+ const col = makeCol({ name: 'name', field: '$.metadata.name' });
427
+ const result = headerFromSchemaCol(col as any, makeGetters() as any, true, null as any);
428
+
429
+ expect(result.sort).toStrictEqual(['$.metadata.name']);
430
+ expect(result.search).toStrictEqual('$.metadata.name');
431
+ });
432
+ });
433
+ });
package/store/catalog.js CHANGED
@@ -302,6 +302,16 @@ export const mutations = {
302
302
  state.namespacedRepos = namespaced;
303
303
  },
304
304
 
305
+ addClusterRepo(state, repo) {
306
+ if (!state.clusterRepos) {
307
+ state.clusterRepos = [];
308
+ }
309
+
310
+ if (!state.clusterRepos.find((r) => r.metadata?.name === repo.metadata?.name)) {
311
+ state.clusterRepos.push(repo);
312
+ }
313
+ },
314
+
305
315
  setCharts(state, { charts, errors = [], loaded = [] }) {
306
316
  state.charts = charts;
307
317
  state.errors = errors;
@@ -445,6 +455,53 @@ export const actions = {
445
455
  }
446
456
  },
447
457
 
458
+ async loadRepo(ctx, { repoName }) {
459
+ const {
460
+ state, getters, rootGetters, commit, dispatch
461
+ } = ctx;
462
+
463
+ const inStore = rootGetters['currentCluster'] ? rootGetters['currentProduct'].inStore : 'management';
464
+
465
+ let repo = rootGetters[`${ inStore }/byId`](CATALOG.CLUSTER_REPO, repoName);
466
+
467
+ if (!repo) {
468
+ try {
469
+ repo = await dispatch(`${ inStore }/find`, { type: CATALOG.CLUSTER_REPO, id: repoName }, { root: true });
470
+ } catch (e) {
471
+ return;
472
+ }
473
+ }
474
+
475
+ if (!repo) {
476
+ return;
477
+ }
478
+
479
+ commit('addClusterRepo', repo);
480
+
481
+ if (getters.isLoaded(repo)) {
482
+ return;
483
+ }
484
+
485
+ try {
486
+ const index = await repo.followLink('index');
487
+ const charts = { ...state.charts };
488
+
489
+ for (const k in index?.entries) {
490
+ for (const entry of index.entries[k]) {
491
+ addChart(ctx, charts, entry, repo);
492
+ }
493
+ }
494
+
495
+ commit('setCharts', {
496
+ charts,
497
+ errors: state.errors,
498
+ loaded: [repo],
499
+ });
500
+ } catch (e) {
501
+ console.error(`Failed to load repo ${ repoName }:`, e); // eslint-disable-line no-console
502
+ }
503
+ },
504
+
448
505
  /**
449
506
  * Globally refreshes all loaded repositories by triggering their refresh actions concurrently,
450
507
  * bypassing individual catalog loads, and then performs a single, global catalog/load.
package/store/features.js CHANGED
@@ -39,6 +39,10 @@ export const PROVISIONING_PRE_BOOTSTRAP = create('provisioningprebootstrap', fal
39
39
  export const SCHEDULING_CUSTOMIZATION = create(SCHEDULING_CUSTOMIZATION_FEATURE, false);
40
40
  export const SCC = create('rancher-scc-registration-extension', true);
41
41
  export const AUTOSCALER = create('cluster-autoscaling', false);
42
+ // Feature flags for disabling shell access to clusters, nodes, pods
43
+ export const CLUSTER_SHELL = create('cluster-shell', true);
44
+ export const NODE_SHELL = create('node-shell', true);
45
+ export const POD_SHELL = create('pod-shell', true);
42
46
 
43
47
  // Not currently used.. no point defining ones we don't use
44
48
  // export const EMBEDDED_CLUSTER_API = create('embedded-cluster-api', true);
package/store/plugins.js CHANGED
@@ -172,14 +172,14 @@ export const getters = {
172
172
  return async(name) => {
173
173
  const schema = getters.schemaForDriver(name);
174
174
 
175
- await schema.fetchResourceFields();
176
-
177
175
  if ( !schema ) {
178
176
  // eslint-disable-next-line no-console
179
177
  console.error(`Machine Driver Config schema not found for ${ name }`);
180
178
 
181
179
  return [];
182
180
  }
181
+ await schema.fetchResourceFields();
182
+
183
183
  // This is used in places where `createPopulated` has been called, which has called fetchResourceFields to populate resourceFields
184
184
  const out = Object.keys(schema?.resourceFields || {});
185
185
 
@@ -191,13 +191,16 @@ export const getters = {
191
191
 
192
192
  fieldsForDriver(state, getters) {
193
193
  return async(name) => {
194
+ const out = {};
194
195
  const schema = getters.schemaForDriver(name);
195
196
 
197
+ if ( !schema ) {
198
+ return out;
199
+ }
200
+
196
201
  await schema.fetchResourceFields();
197
202
  const names = await getters.fieldNamesForDriver(name);
198
203
 
199
- const out = {};
200
-
201
204
  for ( const n of names ) {
202
205
  out[n] = schema.resourceFields[n];
203
206
  }
@@ -0,0 +1,5 @@
1
+ export interface ButtonGroupOption {
2
+ labelKey: string;
3
+ value: string;
4
+ disabled?: boolean;
5
+ }