@rancher/shell 3.0.5-rc.5 → 3.0.5-rc.6

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 (312) hide show
  1. package/assets/data/aws-regions.json +1 -0
  2. package/assets/images/key.svg +17 -0
  3. package/assets/styles/base/_spacing.scss +2 -2
  4. package/assets/styles/global/_form.scss +1 -1
  5. package/assets/styles/global/_labeled-input.scss +1 -1
  6. package/assets/styles/themes/_dark.scss +3 -0
  7. package/assets/styles/themes/_light.scss +3 -0
  8. package/assets/styles/vendor/vue-select.scss +1 -1
  9. package/assets/translations/en-us.yaml +404 -64
  10. package/assets/translations/zh-hans.yaml +3 -4
  11. package/cloud-credential/gcp.vue +9 -1
  12. package/components/AppModal.vue +2 -0
  13. package/components/CodeMirror.vue +1 -1
  14. package/components/ConfigMapSettings/Settings.vue +377 -0
  15. package/components/ConfigMapSettings/index.vue +354 -0
  16. package/components/CruResource.vue +1 -2
  17. package/components/DetailText.vue +61 -11
  18. package/components/Drawer/Chrome.vue +116 -0
  19. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +61 -0
  20. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +48 -0
  21. package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +54 -0
  22. package/components/Drawer/ResourceDetailDrawer/__tests__/YamlTab.test.ts +80 -0
  23. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +82 -0
  24. package/components/Drawer/ResourceDetailDrawer/__tests__/helpers.test.ts +42 -0
  25. package/components/Drawer/ResourceDetailDrawer/composables.ts +50 -0
  26. package/components/Drawer/ResourceDetailDrawer/helpers.ts +10 -0
  27. package/components/Drawer/ResourceDetailDrawer/index.vue +110 -0
  28. package/components/GrowlManager.vue +16 -15
  29. package/components/IconOrSvg.vue +5 -0
  30. package/components/KeyValueView.vue +1 -1
  31. package/components/LocaleSelector.vue +9 -1
  32. package/components/ProgressBarMulti.vue +1 -0
  33. package/components/PromptModal.vue +6 -1
  34. package/components/RelatedResources.vue +4 -12
  35. package/components/Resource/Detail/Additional.vue +46 -0
  36. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  37. package/components/Resource/Detail/Metadata/Annotations/index.vue +5 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +223 -0
  39. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +37 -254
  40. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +298 -0
  41. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +27 -5
  42. package/components/Resource/Detail/Metadata/KeyValue.vue +25 -17
  43. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -1
  44. package/components/Resource/Detail/Metadata/Labels/index.vue +4 -0
  45. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +1 -1
  46. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +1 -1
  47. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +75 -0
  48. package/components/Resource/Detail/Metadata/composables.ts +60 -11
  49. package/components/Resource/Detail/Metadata/index.vue +12 -5
  50. package/components/Resource/Detail/Page.vue +15 -0
  51. package/components/Resource/Detail/ResourceRow.vue +37 -18
  52. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/composables.test.ts +29 -0
  53. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/index.test.ts +48 -0
  54. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/composables.ts +31 -0
  55. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/index.vue +50 -0
  56. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/__tests__/composables.test.ts +66 -0
  57. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/composables.ts +21 -0
  58. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/index.vue +31 -0
  59. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Basic.vue +45 -0
  60. package/components/Resource/Detail/ResourceTabs/SecretDataTab/BasicAuth.vue +31 -0
  61. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Certificate.vue +31 -0
  62. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Registry.vue +22 -0
  63. package/components/Resource/Detail/ResourceTabs/SecretDataTab/ServiceAccountToken.vue +31 -0
  64. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Ssh.vue +32 -0
  65. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Basic.test.ts +40 -0
  66. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/BasicAuth.test.ts +33 -0
  67. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Certificate.test.ts +33 -0
  68. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Registry.test.ts +27 -0
  69. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/ServiceAccountToken.test.ts +33 -0
  70. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Ssh.test.ts +33 -0
  71. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/auth-types.test.ts +186 -0
  72. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/composables.test.ts +102 -0
  73. package/components/Resource/Detail/ResourceTabs/SecretDataTab/auth-types.ts +109 -0
  74. package/components/Resource/Detail/ResourceTabs/SecretDataTab/composeables.ts +52 -0
  75. package/components/Resource/Detail/ResourceTabs/SecretDataTab/index.vue +71 -0
  76. package/components/Resource/Detail/TitleBar/Title.vue +2 -1
  77. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +1 -1
  78. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +1 -1
  79. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +63 -0
  80. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  81. package/components/Resource/Detail/TitleBar/composables.ts +44 -0
  82. package/components/Resource/Detail/TitleBar/index.vue +83 -11
  83. package/components/Resource/Detail/composables.ts +45 -0
  84. package/components/ResourceDetail/Masthead/__tests__/index.test.ts +70 -0
  85. package/components/ResourceDetail/{__tests__/Masthead.test.ts → Masthead/__tests__/legacy.test.ts} +3 -3
  86. package/components/ResourceDetail/Masthead/index.vue +65 -0
  87. package/components/ResourceDetail/Masthead/latest.vue +44 -0
  88. package/components/ResourceDetail/__tests__/index.test.ts +26 -5
  89. package/components/ResourceDetail/index.vue +30 -16
  90. package/components/ResourceDetail/legacy.vue +18 -1
  91. package/components/ResourceList/Masthead.vue +6 -0
  92. package/components/ResourceYaml.vue +14 -1
  93. package/components/SlideInPanelManager.vue +46 -7
  94. package/components/StateDot/index.vue +28 -0
  95. package/components/Tabbed/index.vue +11 -15
  96. package/components/Wizard.vue +4 -2
  97. package/components/__tests__/ConfigMapSettings.test.ts +376 -0
  98. package/components/__tests__/GrowlManager.test.ts +0 -25
  99. package/components/auth/login/ldap.vue +1 -1
  100. package/components/fleet/FleetApplications.vue +0 -7
  101. package/components/fleet/FleetClusterTargets/TargetsList.vue +66 -0
  102. package/components/fleet/FleetClusterTargets/index.vue +455 -0
  103. package/components/fleet/FleetClusters.vue +25 -6
  104. package/components/fleet/FleetGitRepoPaths.vue +476 -0
  105. package/components/fleet/FleetHelmOps.vue +8 -0
  106. package/components/fleet/FleetRepos.vue +1 -6
  107. package/components/fleet/FleetResources.vue +4 -5
  108. package/components/fleet/FleetValuesFrom.vue +295 -0
  109. package/components/fleet/__tests__/FleetClusterTargets.test.ts +1224 -0
  110. package/components/fleet/__tests__/FleetGitRepoPaths.test.ts +265 -0
  111. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +13 -13
  112. package/components/fleet/__tests__/FleetValuesFrom.test.ts +300 -0
  113. package/components/fleet/dashboard/ResourceCard.vue +1 -0
  114. package/components/fleet/dashboard/ResourceCardSummary.vue +1 -5
  115. package/components/fleet/dashboard/ResourceDetails.vue +8 -10
  116. package/components/fleet/dashboard/ResourcePanel.vue +15 -8
  117. package/components/form/ArrayList.vue +13 -2
  118. package/components/form/ChangePassword.vue +3 -1
  119. package/components/form/Footer.vue +10 -4
  120. package/components/form/KeyValue.vue +81 -43
  121. package/components/form/LabeledSelect.vue +56 -16
  122. package/components/form/Labels.vue +90 -17
  123. package/components/form/MatchExpressions.vue +46 -5
  124. package/components/form/NameNsDescription.vue +1 -1
  125. package/components/form/ResourceSelector.vue +1 -0
  126. package/components/form/ResourceTabs/index.vue +5 -0
  127. package/components/form/SecretSelector.vue +9 -2
  128. package/components/form/Select.vue +57 -19
  129. package/components/form/SimpleSecretSelector.vue +9 -2
  130. package/components/form/Taints.vue +21 -2
  131. package/components/form/UnitInput.vue +8 -0
  132. package/components/form/ValueFromResource.vue +1 -1
  133. package/components/form/__tests__/LabeledSelect.test.ts +8 -4
  134. package/components/form/__tests__/Labels.test.ts +360 -0
  135. package/components/form/__tests__/MatchExpressions.test.ts +16 -13
  136. package/components/form/__tests__/Select.test.ts +5 -2
  137. package/components/formatter/FleetApplicationSource.vue +1 -1
  138. package/components/formatter/WorkloadHealthScale.vue +1 -1
  139. package/components/google/AccountAccess.vue +211 -0
  140. package/components/google/types/gcp.d.ts +136 -0
  141. package/components/google/types/index.d.ts +101 -0
  142. package/components/google/util/__mocks__/gcp.ts +465 -0
  143. package/components/google/util/formatter.ts +82 -0
  144. package/components/google/util/gcp.ts +134 -0
  145. package/components/google/util/index.d.ts +11 -0
  146. package/components/nav/Favorite.vue +1 -1
  147. package/components/nav/Group.vue +70 -47
  148. package/components/nav/Header.vue +5 -1
  149. package/components/nav/NamespaceFilter.vue +13 -1
  150. package/components/nav/NotificationCenter/Notification.vue +510 -0
  151. package/components/nav/NotificationCenter/NotificationHeader.vue +112 -0
  152. package/components/nav/NotificationCenter/index.vue +148 -0
  153. package/composables/drawer.ts +26 -0
  154. package/composables/resources.test.ts +63 -0
  155. package/composables/resources.ts +38 -0
  156. package/composables/useIsNewDetailPageEnabled.ts +17 -0
  157. package/config/labels-annotations.js +6 -0
  158. package/config/product/auth.js +16 -1
  159. package/config/product/{cis.js → compliance.js} +23 -26
  160. package/config/product/explorer.js +5 -1
  161. package/config/product/fleet.js +7 -0
  162. package/config/product/settings.js +22 -11
  163. package/config/query-params.js +3 -0
  164. package/config/roles.ts +1 -1
  165. package/config/router/navigation-guards/authentication.js +51 -2
  166. package/config/router/routes.js +27 -31
  167. package/config/settings.ts +21 -3
  168. package/config/store.js +2 -0
  169. package/config/system-namespaces.js +1 -1
  170. package/config/table-headers.js +2 -2
  171. package/config/types.js +15 -6
  172. package/core/plugin.ts +32 -7
  173. package/core/types.ts +18 -1
  174. package/detail/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +22 -18
  175. package/detail/management.cattle.io.fleetworkspace.vue +18 -27
  176. package/detail/management.cattle.io.oidcclient.vue +369 -0
  177. package/detail/node.vue +2 -2
  178. package/detail/pod.vue +2 -2
  179. package/detail/service.vue +10 -1
  180. package/detail/workload/index.vue +8 -2
  181. package/dialog/ExtensionCatalogUninstallDialog.vue +7 -4
  182. package/dialog/GenericPrompt.vue +1 -1
  183. package/dialog/ImportDialog.vue +8 -8
  184. package/dialog/OidcClientSecretDialog.vue +117 -0
  185. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +3 -3
  186. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +5 -2
  187. package/edit/autoscaling.horizontalpodautoscaler/index.vue +4 -1
  188. package/edit/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +30 -31
  189. package/edit/{cis.cattle.io.clusterscanbenchmark.vue → compliance.cattle.io.clusterscanbenchmark.vue} +4 -4
  190. package/edit/{cis.cattle.io.clusterscanprofile.vue → compliance.cattle.io.clusterscanprofile.vue} +5 -5
  191. package/edit/configmap.vue +4 -1
  192. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  193. package/edit/fleet.cattle.io.gitrepo.vue +44 -222
  194. package/edit/fleet.cattle.io.helmop.vue +44 -269
  195. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  196. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  197. package/edit/logging-flow/index.vue +1 -0
  198. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  199. package/edit/management.cattle.io.fleetworkspace.vue +1 -0
  200. package/edit/management.cattle.io.oidcclient.vue +162 -0
  201. package/edit/management.cattle.io.project.vue +4 -1
  202. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  203. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +5 -0
  204. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  205. package/edit/monitoring.coreos.com.receiver/auth.vue +30 -30
  206. package/edit/monitoring.coreos.com.receiver/index.vue +1 -0
  207. package/edit/monitoring.coreos.com.receiver/types/email.vue +1 -1
  208. package/edit/monitoring.coreos.com.route.vue +1 -0
  209. package/edit/namespace.vue +1 -0
  210. package/edit/networking.istio.io.destinationrule/index.vue +4 -1
  211. package/edit/networking.k8s.io.ingress/index.vue +4 -1
  212. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +7 -2
  213. package/edit/networking.k8s.io.networkpolicy/index.vue +6 -2
  214. package/edit/node.vue +1 -0
  215. package/edit/persistentvolume/index.vue +4 -1
  216. package/edit/provisioning.cattle.io.cluster/rke2.vue +418 -382
  217. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +27 -27
  218. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +5 -0
  219. package/edit/resources.cattle.io.restore.vue +1 -1
  220. package/edit/secret/index.vue +1 -0
  221. package/edit/service.vue +4 -1
  222. package/edit/serviceaccount.vue +4 -1
  223. package/edit/storage.k8s.io.storageclass/index.vue +4 -1
  224. package/edit/workload/index.vue +5 -0
  225. package/list/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +2 -2
  226. package/list/management.cattle.io.oidcclient.vue +108 -0
  227. package/list/node.vue +2 -0
  228. package/machine-config/amazonec2.vue +3 -24
  229. package/machine-config/components/GCEImage.vue +374 -0
  230. package/machine-config/google.vue +617 -0
  231. package/mixins/__tests__/brand.spec.ts +170 -0
  232. package/mixins/brand.js +16 -17
  233. package/mixins/create-edit-view/index.js +5 -0
  234. package/mixins/resource-fetch-api-pagination.js +16 -0
  235. package/mixins/vue-select-overrides.js +1 -0
  236. package/models/{cis.cattle.io.clusterscan.js → compliance.cattle.io.clusterscan.js} +8 -8
  237. package/models/{cis.cattle.io.clusterscanbenchmark.js → compliance.cattle.io.clusterscanbenchmark.js} +1 -1
  238. package/models/{cis.cattle.io.clusterscanprofile.js → compliance.cattle.io.clusterscanprofile.js} +5 -5
  239. package/models/{cis.cattle.io.clusterscanreport.js → compliance.cattle.io.clusterscanreport.js} +1 -1
  240. package/models/fleet-application.js +8 -79
  241. package/models/fleet.cattle.io.cluster.js +11 -0
  242. package/models/fleet.cattle.io.gitrepo.js +2 -2
  243. package/models/fleet.cattle.io.helmop.js +9 -39
  244. package/models/management.cattle.io.fleetworkspace.js +2 -1
  245. package/models/management.cattle.io.oidcclient.js +18 -0
  246. package/models/management.cattle.io.registration.js +3 -0
  247. package/models/provisioning.cattle.io.cluster.js +5 -5
  248. package/models/service.js +4 -0
  249. package/models/workload.js +5 -0
  250. package/package.json +1 -1
  251. package/pages/about.vue +4 -58
  252. package/pages/auth/login.vue +1 -1
  253. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +0 -1
  254. package/pages/c/_cluster/apps/charts/index.vue +285 -81
  255. package/pages/c/_cluster/auth/user.retention/index.vue +87 -78
  256. package/pages/c/_cluster/explorer/index.vue +3 -3
  257. package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -1
  258. package/pages/c/_cluster/fleet/application/create.vue +3 -2
  259. package/pages/c/_cluster/fleet/index.vue +94 -56
  260. package/pages/c/_cluster/fleet/settings/index.vue +229 -0
  261. package/pages/c/_cluster/longhorn/index.vue +5 -2
  262. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +16 -1
  263. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -2
  264. package/pages/explorer/resource/detail/configmap.vue +30 -7
  265. package/pages/explorer/resource/detail/secret.vue +50 -0
  266. package/pages/home.vue +9 -55
  267. package/pages/support/index.vue +4 -6
  268. package/plugins/dashboard-store/actions.js +19 -5
  269. package/plugins/dashboard-store/getters.js +4 -0
  270. package/plugins/dashboard-store/resource-class.js +16 -2
  271. package/plugins/steve/steve-pagination-utils.ts +26 -18
  272. package/plugins/steve/subscribe.js +6 -1
  273. package/rancher-components/Banner/Banner.vue +13 -0
  274. package/rancher-components/Form/Checkbox/Checkbox.vue +9 -4
  275. package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
  276. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -0
  277. package/rancher-components/RcItemCard/RcItemCard.vue +8 -3
  278. package/store/auth.js +2 -0
  279. package/store/catalog.js +23 -1
  280. package/store/growl.js +97 -8
  281. package/store/index.js +6 -0
  282. package/store/notifications.ts +426 -0
  283. package/store/prefs.js +0 -1
  284. package/store/type-map.js +19 -16
  285. package/store/uiplugins.ts +15 -1
  286. package/types/fleet.d.ts +24 -0
  287. package/types/notifications/index.ts +74 -0
  288. package/types/shell/index.d.ts +46 -32
  289. package/types/store/dashboard-store.types.ts +16 -0
  290. package/utils/__tests__/fleet.test.ts +148 -0
  291. package/utils/__tests__/object.test.ts +54 -1
  292. package/utils/__tests__/string.test.ts +273 -1
  293. package/utils/__tests__/time.test.ts +31 -0
  294. package/utils/auth.js +9 -2
  295. package/utils/crypto/encryption.ts +103 -0
  296. package/utils/cspAdaptor.ts +51 -0
  297. package/utils/fleet.ts +54 -65
  298. package/utils/object.js +36 -0
  299. package/utils/pagination-utils.ts +1 -1
  300. package/utils/release-notes.ts +48 -0
  301. package/utils/selector-typed.ts +7 -2
  302. package/utils/string.js +24 -0
  303. package/utils/{time.js → time.ts} +25 -6
  304. package/utils/uiplugins.ts +22 -0
  305. package/utils/validators/formRules/index.ts +3 -0
  306. package/components/Resource/Detail/TitleBar/composable.ts +0 -31
  307. package/config/product/legacy.js +0 -62
  308. package/pages/c/_cluster/legacy/pages/_page.vue +0 -29
  309. package/pages/c/_cluster/legacy/project/_page.vue +0 -57
  310. package/pages/c/_cluster/legacy/project/index.vue +0 -32
  311. package/pages/c/_cluster/legacy/project/pipelines.vue +0 -96
  312. /package/components/ResourceDetail/{Masthead.vue → Masthead/legacy.vue} +0 -0
@@ -1,4 +1,4 @@
1
- import { decodeHtml, resourceNames } from '@shell/utils/string';
1
+ import { decodeHtml, resourceNames, pathArrayToTree } from '@shell/utils/string';
2
2
 
3
3
  describe('fx: decodeHtml', () => {
4
4
  it('should decode HTML values from escaped string into valid markup', () => {
@@ -89,3 +89,275 @@ describe('fx: resourceNames', () => {
89
89
  });
90
90
  });
91
91
  });
92
+
93
+ describe('fx: pathArrayToTree', () => {
94
+ interface TreeNode {
95
+ name: string;
96
+ children: TreeNode[];
97
+ }
98
+
99
+ function sortTree(nodes: TreeNode[]): void {
100
+ if (!nodes) return;
101
+ nodes.sort((a, b) => a.name.localeCompare(b.name));
102
+ nodes.forEach((node) => {
103
+ if (node.children && node.children.length > 0) {
104
+ sortTree(node.children);
105
+ }
106
+ });
107
+ }
108
+
109
+ it('should return an empty array for an empty input array', () => {
110
+ const paths: string[] = [];
111
+
112
+ expect(pathArrayToTree(paths)).toStrictEqual([]);
113
+ });
114
+
115
+ it('should convert simple single-level paths correctly', () => {
116
+ const paths: string[] = [
117
+ 'file1.txt',
118
+ 'folderA',
119
+ 'itemB'
120
+ ];
121
+ const expected: TreeNode[] = [
122
+ { name: 'file1.txt', children: [] },
123
+ { name: 'folderA', children: [] },
124
+ { name: 'itemB', children: [] },
125
+ ];
126
+ const actual = pathArrayToTree(paths);
127
+
128
+ sortTree(actual); // Sort the actual output for deterministic comparison
129
+ expect(actual).toStrictEqual(expected);
130
+ });
131
+
132
+ it('should convert two-level nested paths correctly', () => {
133
+ const paths: string[] = [
134
+ 'folderA/file1.txt',
135
+ 'folderC/file3.txt'
136
+ ];
137
+ const expected: TreeNode[] = [
138
+ {
139
+ name: 'folderA',
140
+ children: [
141
+ { name: 'file1.txt', children: [] },
142
+ ]
143
+ },
144
+ {
145
+ name: 'folderC',
146
+ children: [
147
+ { name: 'file3.txt', children: [] }
148
+ ]
149
+ },
150
+ ];
151
+ const actual = pathArrayToTree(paths);
152
+
153
+ sortTree(actual);
154
+ expect(actual).toStrictEqual(expected);
155
+ });
156
+
157
+ it('should handle deep nesting and common prefixes correctly', () => {
158
+ const paths: string[] = [
159
+ 'root/level1/level2/file.doc',
160
+ 'root/level1/another_file.txt',
161
+ 'root/diff_level1/config.json'
162
+ ];
163
+ const expected: TreeNode[] = [
164
+ {
165
+ name: 'root',
166
+ children: [
167
+ {
168
+ name: 'diff_level1',
169
+ children: [
170
+ { name: 'config.json', children: [] }
171
+ ]
172
+ },
173
+ {
174
+ name: 'level1',
175
+ children: [
176
+ { name: 'another_file.txt', children: [] },
177
+ {
178
+ name: 'level2',
179
+ children: [
180
+ { name: 'file.doc', children: [] }
181
+ ]
182
+ }
183
+ ]
184
+ }
185
+ ]
186
+ }
187
+ ];
188
+ const actual = pathArrayToTree(paths);
189
+
190
+ sortTree(actual);
191
+ expect(actual).toStrictEqual(expected);
192
+ });
193
+
194
+ it('should handle leading/trailing/multiple slashes by treating them as path segments', () => {
195
+ const paths: string[] = [
196
+ '/path/to/file.txt', // Starts with a slash
197
+ 'path/to/another.txt/', // Ends with a slash (adds an empty segment)
198
+ '//root/item//subitem//', // Multiple slashes (adds empty segments)
199
+ 'root/item/another_subitem'
200
+ ];
201
+ const expected: TreeNode[] = [
202
+ {
203
+ name: '',
204
+ children: [
205
+ {
206
+ name: '',
207
+ children: [
208
+ {
209
+ name: 'root',
210
+ children: [
211
+ {
212
+ name: 'item',
213
+ children: [
214
+ {
215
+ name: '',
216
+ children: [
217
+ {
218
+ name: 'subitem',
219
+ children: [
220
+ {
221
+ name: '',
222
+ children: [
223
+ {
224
+ name: '',
225
+ children: [
226
+ ],
227
+ },
228
+ ],
229
+ },
230
+ ],
231
+ },
232
+ ],
233
+ },
234
+ ],
235
+ },
236
+ ],
237
+ },
238
+ ],
239
+ },
240
+ {
241
+ name: 'path',
242
+ children: [
243
+ {
244
+ name: 'to',
245
+ children: [
246
+ {
247
+ name: 'file.txt',
248
+ children: [
249
+ ],
250
+ },
251
+ ],
252
+ },
253
+ ],
254
+ },
255
+ ],
256
+ },
257
+ {
258
+ name: 'path',
259
+ children: [
260
+ {
261
+ name: 'to',
262
+ children: [
263
+ {
264
+ name: 'another.txt',
265
+ children: [
266
+ {
267
+ name: '',
268
+ children: [
269
+ ],
270
+ },
271
+ ],
272
+ },
273
+ ],
274
+ },
275
+ ],
276
+ },
277
+ {
278
+ name: 'root',
279
+ children: [
280
+ {
281
+ name: 'item',
282
+ children: [
283
+ {
284
+ name: 'another_subitem',
285
+ children: [
286
+ ],
287
+ },
288
+ ],
289
+ },
290
+ ],
291
+ },
292
+ ];
293
+ const actual = pathArrayToTree(paths);
294
+
295
+ sortTree(actual);
296
+ expect(actual).toStrictEqual(expected);
297
+ });
298
+
299
+ it('should produce the same structure regardless of input order', () => {
300
+ const paths1: string[] = ['a/b', 'a/c'];
301
+ const paths2: string[] = ['a/c', 'a/b'];
302
+
303
+ const actual1 = pathArrayToTree(paths1);
304
+
305
+ sortTree(actual1);
306
+ const actual2 = pathArrayToTree(paths2);
307
+
308
+ sortTree(actual2);
309
+
310
+ expect(actual1).toStrictEqual(actual2);
311
+ });
312
+
313
+ it('should handle names with special characters (not slashes) correctly', () => {
314
+ const paths: string[] = [
315
+ 'my-folder/file_name.1.txt',
316
+ 'another folder/item with spaces'
317
+ ];
318
+ const expected: TreeNode[] = [
319
+ {
320
+ name: 'another folder',
321
+ children: [
322
+ { name: 'item with spaces', children: [] }
323
+ ]
324
+ },
325
+ {
326
+ name: 'my-folder',
327
+ children: [
328
+ { name: 'file_name.1.txt', children: [] }
329
+ ]
330
+ }
331
+ ];
332
+ const actual = pathArrayToTree(paths);
333
+
334
+ sortTree(actual);
335
+ expect(actual).toStrictEqual(expected);
336
+ });
337
+
338
+ it('should handle duplicate paths correctly, only adding unique structure', () => {
339
+ const paths: string[] = [
340
+ 'folder/file.txt',
341
+ 'folder/file.txt', // Duplicate
342
+ 'other_folder/another_file.txt'
343
+ ];
344
+ const expected: TreeNode[] = [
345
+ {
346
+ name: 'folder',
347
+ children: [
348
+ { name: 'file.txt', children: [] }
349
+ ]
350
+ },
351
+ {
352
+ name: 'other_folder',
353
+ children: [
354
+ { name: 'another_file.txt', children: [] }
355
+ ]
356
+ }
357
+ ];
358
+ const actual = pathArrayToTree(paths);
359
+
360
+ sortTree(actual);
361
+ expect(actual).toStrictEqual(expected);
362
+ });
363
+ });
@@ -0,0 +1,31 @@
1
+ import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
2
+ import { dateTimeFormat } from '@shell/utils/time';
3
+ import { type Store } from 'vuex';
4
+
5
+ describe('function: dateTimeFormat', () => {
6
+ jest.useFakeTimers()
7
+ .setSystemTime(new Date('21-10-2010 4:29:00'));
8
+ const store = {
9
+ getters: {
10
+ 'prefs/get': (key: string) => {
11
+ return {
12
+ [DATE_FORMAT]: 'ddd, MMM D YYYY',
13
+ [TIME_FORMAT]: 'h:mm:ss a',
14
+ }[key];
15
+ },
16
+ },
17
+ } as Store<any>;
18
+
19
+ it('should format date and time correctly', () => {
20
+ const date = new Date('2010-10-21T04:29:00Z');
21
+ const formattedDate = dateTimeFormat(date.toISOString(), store);
22
+
23
+ expect(formattedDate).toBe('Thu, Oct 21 2010 4:29:00 am');
24
+ });
25
+
26
+ it('should return empty string for undefined value', () => {
27
+ const formattedDate = dateTimeFormat(undefined, store);
28
+
29
+ expect(formattedDate).toBe('');
30
+ });
31
+ });
package/utils/auth.js CHANGED
@@ -8,6 +8,7 @@ import { allHash } from '@shell/utils/promise';
8
8
  import { getProductFromRoute, getResourceFromRoute } from '@shell/utils/router';
9
9
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
10
10
  import { findBy } from '@shell/utils/array';
11
+ import { onExtensionsReady } from '@shell/utils/uiplugins';
11
12
 
12
13
  export const AUTH_BROADCAST_CHANNEL_NAME = 'rancher-auth-test-callback';
13
14
 
@@ -333,9 +334,15 @@ export async function tryInitialSetup(store, password = 'admin') {
333
334
  /**
334
335
  * Record in our state management that we're indeed logged in
335
336
  */
336
- export function isLoggedIn(store, me) {
337
+ export async function isLoggedIn(store, userData) {
337
338
  store.commit('auth/hasAuth', true);
338
- store.commit('auth/loggedInAs', me.id);
339
+ store.commit('auth/loggedInAs', userData.id);
340
+
341
+ // Init the notification center now that we know who the user is
342
+ await store.dispatch('notifications/init', userData);
343
+
344
+ // Let the extensions know that the user is logged in
345
+ await onExtensionsReady(store);
339
346
  }
340
347
 
341
348
  /**
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Helpers for string encryption and decryption
3
+ */
4
+
5
+ // We just use a static salt
6
+ const SALT = new TextEncoder().encode('rancher-dashboard');
7
+
8
+ /**
9
+ * Type for the result of encryption
10
+ */
11
+ export type EncryptedString = {
12
+ cipher: string;
13
+ iv: string;
14
+ };
15
+
16
+ /**
17
+ * Generate an encryption key from a password
18
+ *
19
+ * @param password Password to use for the key
20
+ * @returns Encryption key
21
+ */
22
+ export async function deriveKey(password: string): Promise<CryptoKey> {
23
+ const passwordBytes = stringToBytes(password);
24
+
25
+ const initialKey = await crypto.subtle.importKey(
26
+ 'raw',
27
+ passwordBytes,
28
+ { name: 'PBKDF2' },
29
+ false,
30
+ ['deriveKey']
31
+ );
32
+
33
+ return await crypto.subtle.deriveKey(
34
+ {
35
+ name: 'PBKDF2',
36
+ salt: SALT,
37
+ iterations: 100000,
38
+ hash: 'SHA-256'
39
+ },
40
+ initialKey,
41
+ {
42
+ name: 'AES-GCM',
43
+ length: 256
44
+ },
45
+ false,
46
+ [
47
+ 'encrypt',
48
+ 'decrypt'
49
+ ]
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Encrypt data
55
+ *
56
+ * @param content String to be encrypted
57
+ * @param key Encryption key
58
+ * @returns Encrypted data
59
+ */
60
+ export async function encrypt(content: string, key: CryptoKey): Promise<EncryptedString> {
61
+ const iv = crypto.getRandomValues(new Uint8Array(12));
62
+ const contentBytes = stringToBytes(content);
63
+ const cipher = new Uint8Array(
64
+ await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, contentBytes)
65
+ );
66
+
67
+ return {
68
+ iv: bytesToBase64(iv),
69
+ cipher: bytesToBase64(cipher),
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Decrypt encrypted data
75
+ * @param encryptedData Encrypted data with `cipher` and `iv` field
76
+ * @param key Key to use for decryption
77
+ * @returns Decrypted value as a string
78
+ */
79
+ export async function decrypt(encryptedData: EncryptedString, key: CryptoKey): Promise<string> {
80
+ const iv = base64ToBytes(encryptedData.iv);
81
+ const cipher = base64ToBytes(encryptedData.cipher);
82
+ const contentBytes = new Uint8Array(
83
+ await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, cipher)
84
+ );
85
+
86
+ return bytesToString(contentBytes);
87
+ }
88
+
89
+ function bytesToString(bytes: any): string {
90
+ return new TextDecoder().decode(bytes);
91
+ }
92
+
93
+ function stringToBytes(str: string): Uint8Array {
94
+ return new TextEncoder().encode(str);
95
+ }
96
+
97
+ function bytesToBase64(arr: Uint8Array) {
98
+ return btoa(Array.from(arr, (b: number) => String.fromCharCode(b)).join(''));
99
+ }
100
+
101
+ function base64ToBytes(base64: string): Uint8Array {
102
+ return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
103
+ }
@@ -0,0 +1,51 @@
1
+ // For testing these could be changed to something like...
2
+
3
+ import { CATALOG } from '@shell/config/types';
4
+ import { FilterArgs, PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
5
+ import { VuexStore } from '@shell/types/store/vuex';
6
+
7
+ const CSP_ADAPTER_APPS = ['rancher-csp-adapter', 'rancher-csp-billing-adapter'];
8
+ // For testing above line could be replaced with below line...
9
+ // const cspAdaptorApp = ['rancher-webhooka', 'rancher-webhook'];
10
+
11
+ /**
12
+ * Helpers in order to
13
+ */
14
+ class CspAdapterUtils {
15
+ static canPagination($store: VuexStore): boolean {
16
+ return $store.getters[`management/paginationEnabled`]({ id: CATALOG.APP, context: 'branding' });
17
+ }
18
+
19
+ static fetchCspAdaptorApp($store: VuexStore): Promise<any> {
20
+ // For the login page, the schemas won't be loaded - we don't need the apps in this case
21
+ if ($store.getters['management/canList'](CATALOG.APP)) {
22
+ if (CspAdapterUtils.canPagination($store)) {
23
+ // Restrict the amount of apps we need to fetch
24
+ return $store.dispatch('management/findPage', {
25
+ type: CATALOG.APP,
26
+ opt: { // Of type ActionFindPageArgs
27
+ pagination: new FilterArgs({
28
+ filters: PaginationParamFilter.createMultipleFields(CSP_ADAPTER_APPS.map(
29
+ (t) => new PaginationFilterField({
30
+ field: 'metadata.name',
31
+ value: t,
32
+ })
33
+ )),
34
+ })
35
+ }
36
+ });
37
+ }
38
+
39
+ return $store.dispatch('management/findAll', { type: CATALOG.APP });
40
+ }
41
+
42
+ return Promise.resolve([]);
43
+ }
44
+
45
+ static hasCspAdapter({ $store, apps }: { $store: VuexStore, apps: any[]}): Object {
46
+ // In theory this should contain the filtered apps when pagination is on, and all apps when off. Keep filtering though in both cases just in case
47
+ return apps?.find((a) => CSP_ADAPTER_APPS.includes(a.metadata?.name));
48
+ }
49
+ }
50
+
51
+ export default CspAdapterUtils;
package/utils/fleet.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { isEmpty, isEqual } from 'lodash';
1
2
  import {
2
3
  BundleDeploymentResource,
3
4
  BundleResourceKey,
@@ -6,10 +7,10 @@ import {
6
7
  Condition,
7
8
  } from '@shell/types/resources/fleet';
8
9
  import { mapStateToEnum, STATES_ENUM, STATES } from '@shell/plugins/dashboard-store/resource-class';
9
- import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
10
+ import { FLEET as FLEET_LABELS, CAPI } from '@shell/config/labels-annotations';
10
11
  import { NAME as EXPLORER_NAME } from '@shell/config/product/explorer';
11
- import { FleetDashboardState, FleetResourceState } from '@shell/types/fleet';
12
- import { FLEET } from '@shell/config/types';
12
+ import { FleetDashboardState, FleetResourceState, Target, TargetMode } from '@shell/types/fleet';
13
+ import { FLEET, VIRTUAL_HARVESTER_PROVIDER } from '@shell/config/types';
13
14
 
14
15
  interface Resource extends BundleDeploymentResource {
15
16
  state: string,
@@ -19,17 +20,6 @@ type Labels = {
19
20
  [key: string]: string,
20
21
  }
21
22
 
22
- interface KeyRef {
23
- key: string;
24
- name: string;
25
- namespace?: string;
26
- }
27
-
28
- interface ValueFrom {
29
- configMapKeyRef?: KeyRef;
30
- secretKeyRef?: KeyRef;
31
- }
32
-
33
23
  function resourceKey(r: BundleResourceKey): string {
34
24
  return `${ r.kind }/${ r.namespace }/${ r.name }`;
35
25
  }
@@ -42,64 +32,63 @@ function conditionIsTrue(conditions: Condition[] | undefined, type: string): boo
42
32
  return !!conditions.find((c) => c.type === type && c.status.toLowerCase() === 'true');
43
33
  }
44
34
 
45
- class HelmOp {
46
- fromValuesFrom(data: ValueFrom[]): { valueFrom: ValueFrom }[] {
47
- return (data || []).map((elem) => {
48
- const out = {} as any;
35
+ class Application {
36
+ excludeHarvesterRule = {
37
+ clusterSelector: {
38
+ matchExpressions: [{
39
+ key: CAPI.PROVIDER,
40
+ operator: 'NotIn',
41
+ values: [
42
+ VIRTUAL_HARVESTER_PROVIDER
43
+ ],
44
+ }],
45
+ },
46
+ };
47
+
48
+ getTargetMode(targets: Target[], namespace: string): TargetMode {
49
+ if (namespace === 'fleet-local') {
50
+ return 'local';
51
+ }
49
52
 
50
- const cm = elem.configMapKeyRef;
53
+ if (!targets.length) {
54
+ return 'none';
55
+ }
51
56
 
52
- if (cm) {
53
- out.valueFrom = {
54
- configMapKeyRef: {
55
- key: cm.key || '',
56
- name: cm.name || '',
57
- }
58
- };
57
+ let mode: TargetMode = 'all';
58
+
59
+ for (const target of targets) {
60
+ const {
61
+ clusterName,
62
+ clusterSelector,
63
+ clusterGroup,
64
+ clusterGroupSelector,
65
+ } = target;
66
+
67
+ if (clusterGroup || clusterGroupSelector) {
68
+ return 'advanced';
59
69
  }
60
70
 
61
- const sc = elem.secretKeyRef;
71
+ if (clusterName) {
72
+ mode = 'clusters';
73
+ }
62
74
 
63
- if (sc) {
64
- out.valueFrom = {
65
- secretKeyRef: {
66
- key: sc.key || '',
67
- name: sc.name || '',
68
- }
69
- };
75
+ if (!isEmpty(clusterSelector)) {
76
+ mode = 'clusters';
70
77
  }
78
+ }
71
79
 
72
- return out;
80
+ const normalized = [...targets].map((target) => {
81
+ delete target.name;
82
+
83
+ return target;
73
84
  });
74
- }
75
85
 
76
- toValuesFrom(data: { valueFrom: ValueFrom }[], namespace: string): ValueFrom[] {
77
- return (data || [])
78
- .filter((f) => f.valueFrom?.configMapKeyRef || f.valueFrom?.secretKeyRef)
79
- .map(({ valueFrom }) => {
80
- const cm = valueFrom.configMapKeyRef;
81
- const sc = valueFrom.secretKeyRef;
82
-
83
- const out = {} as ValueFrom;
84
-
85
- if (cm?.name) {
86
- out.configMapKeyRef = {
87
- key: cm.key,
88
- name: cm.name,
89
- namespace
90
- };
91
- }
92
-
93
- if (sc?.name) {
94
- out.secretKeyRef = {
95
- key: sc.key,
96
- name: sc.name,
97
- namespace
98
- };
99
- }
100
-
101
- return out;
102
- });
86
+ // Check if targets contains only harvester rule after name normalizing
87
+ if (isEqual(normalized, [this.excludeHarvesterRule])) {
88
+ mode = 'all';
89
+ }
90
+
91
+ return mode;
103
92
  }
104
93
  }
105
94
 
@@ -142,14 +131,14 @@ class Fleet {
142
131
  {
143
132
  index: 3,
144
133
  id: 'info',
145
- label: 'InProgress',
134
+ label: 'Pending',
146
135
  color: '#3d98d3',
147
136
  icon: 'icon icon-warning',
148
137
  stateBackground: 'bg-info'
149
138
  },
150
139
  ];
151
140
 
152
- HelmOp = new HelmOp();
141
+ Application = new Application();
153
142
 
154
143
  GIT_HTTPS_REGEX = /^https?:\/\/github\.com\/(.*?)(\.git)?\/*$/;
155
144
  GIT_SSH_REGEX = /^git@github\.com:.*\.git$/;
package/utils/object.js CHANGED
@@ -527,3 +527,39 @@ export function mergeWithReplace(
527
527
  }
528
528
  });
529
529
  }
530
+ /**
531
+ * Converts Object into a string of a format "key1, val1, key2, val2"
532
+ * @param {Object} input - KV object to convert
533
+ * @returns string
534
+ */
535
+ export const convertKVToString = (input) => {
536
+ if (!input || typeof input !== 'object') return '';
537
+
538
+ return Object.entries(input)
539
+ .flatMap(([key, value]) => [key, String(value)])
540
+ .join(',');
541
+ };
542
+
543
+ /**
544
+ * Converts kv string into object
545
+ * @param {string} input - a string of a format "key1, val1, key2, val2"
546
+ * @returns
547
+ */
548
+ export const convertStringToKV = ( input ) => {
549
+ if (!input?.trim()) return {};
550
+
551
+ const parts = input.split(',').map((part) => part.trim());
552
+ const result = {};
553
+
554
+ for (let i = 0; i < parts.length - 1; i += 2) {
555
+ const key = parts[i];
556
+ const value = parts[i + 1];
557
+
558
+ // Accept empty keys but ignore undefined values
559
+ if (typeof key === 'string') {
560
+ result[key] = value ?? '';
561
+ }
562
+ }
563
+
564
+ return result;
565
+ };