@rancher/shell 1.2.0 → 2.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 (689) hide show
  1. package/assets/data/aws-regions.json +9 -0
  2. package/assets/images/vendor/openid.svg +18 -0
  3. package/assets/styles/app.scss +1 -2
  4. package/assets/styles/fonts/_icons.scss +3 -3
  5. package/assets/styles/global/_columns.scss +1 -1
  6. package/assets/styles/global/_labeled-input.scss +1 -0
  7. package/assets/styles/global/_layout.scss +99 -0
  8. package/assets/styles/themes/_csp.scss +2 -2
  9. package/assets/styles/themes/_dark.scss +8 -2
  10. package/assets/styles/themes/_light.scss +2 -1
  11. package/assets/styles/themes/_suse.scss +1 -1
  12. package/assets/styles/vendor/vue-select.scss +5 -0
  13. package/assets/translations/en-us.yaml +299 -63
  14. package/assets/translations/zh-hans.yaml +7 -31
  15. package/babel.config.js +8 -2
  16. package/chart/__tests__/S3.test.ts +9 -2
  17. package/chart/gatekeeper.vue +2 -11
  18. package/chart/istio.vue +1 -10
  19. package/chart/logging/index.vue +2 -11
  20. package/chart/monitoring/alerting/index.vue +7 -21
  21. package/chart/monitoring/grafana/index.vue +61 -2
  22. package/chart/monitoring/index.vue +52 -26
  23. package/chart/monitoring/prometheus/index.vue +39 -45
  24. package/chart/rancher-backup/S3.vue +11 -9
  25. package/chart/rancher-backup/index.vue +18 -15
  26. package/cloud-credential/__tests__/harvester.test.ts +18 -0
  27. package/cloud-credential/azure.vue +4 -17
  28. package/cloud-credential/generic.vue +18 -9
  29. package/cloud-credential/harvester.vue +11 -3
  30. package/components/AppModal.vue +167 -0
  31. package/components/AssignTo.vue +7 -4
  32. package/components/AsyncButton.vue +27 -5
  33. package/components/BackLink.vue +4 -4
  34. package/components/BannerGraphic.vue +1 -0
  35. package/components/BrandImage.vue +47 -1
  36. package/components/Carousel.vue +15 -8
  37. package/components/Certificates.vue +161 -0
  38. package/components/ClusterBadge.vue +12 -3
  39. package/components/ClusterIconMenu.vue +55 -12
  40. package/components/ClusterProviderIcon.vue +14 -3
  41. package/components/CodeMirror.vue +111 -17
  42. package/components/CommunityLinks.vue +12 -8
  43. package/components/CopyCode.vue +6 -2
  44. package/components/CopyToClipboard.vue +2 -1
  45. package/components/CopyToClipboardText.vue +14 -9
  46. package/components/CreateDriver.vue +81 -0
  47. package/components/CruResource.vue +52 -27
  48. package/components/DetailTop.vue +2 -2
  49. package/components/Dialog.vue +6 -5
  50. package/components/DisableAuthProviderModal.vue +14 -8
  51. package/components/DraggableZone.vue +2 -2
  52. package/components/EtcdInfoBanner.vue +5 -5
  53. package/components/ExplorerMembers.vue +3 -3
  54. package/components/ExplorerProjectsNamespaces.vue +31 -7
  55. package/components/FixedBanner.vue +48 -36
  56. package/components/GlobalRoleBindings.vue +26 -0
  57. package/components/IconOrSvg.vue +1 -1
  58. package/components/Import.vue +10 -6
  59. package/components/Inactivity.vue +1 -5
  60. package/components/KeyValueView.vue +14 -10
  61. package/components/Markdown.vue +16 -12
  62. package/components/MessageLink.vue +2 -2
  63. package/components/ModalWithCard.vue +5 -8
  64. package/components/MoveModal.vue +35 -33
  65. package/components/PodSecurityAdmission.vue +3 -3
  66. package/components/PromptChangePassword.vue +33 -33
  67. package/components/PromptModal.vue +11 -21
  68. package/components/PromptRemove.vue +11 -17
  69. package/components/PromptRestore.vue +18 -16
  70. package/components/Questions/__tests__/Boolean.test.ts +9 -19
  71. package/components/Questions/__tests__/Float.test.ts +9 -19
  72. package/components/Questions/__tests__/Int.test.ts +9 -19
  73. package/components/Questions/__tests__/String.test.ts +9 -19
  74. package/components/Questions/__tests__/Yaml.test.ts +9 -20
  75. package/components/Questions/__tests__/utils/questions-defaults.ts +20 -0
  76. package/components/Questions/index.vue +19 -3
  77. package/components/ResourceCancelModal.vue +34 -29
  78. package/components/ResourceDetail/Masthead.vue +48 -16
  79. package/components/ResourceDetail/index.vue +6 -4
  80. package/components/ResourceList/Masthead.vue +10 -9
  81. package/components/ResourceList/index.vue +65 -14
  82. package/components/ResourceTable.vue +87 -21
  83. package/components/ResourceYaml.vue +35 -5
  84. package/components/SelectIconGrid.vue +3 -3
  85. package/components/SideNav.vue +50 -94
  86. package/components/SingleClusterInfo.vue +4 -4
  87. package/components/SortableTable/THead.vue +33 -21
  88. package/components/SortableTable/filtering.js +9 -1
  89. package/components/SortableTable/grouping.js +8 -1
  90. package/components/SortableTable/index.vue +143 -44
  91. package/components/SortableTable/paging.js +36 -7
  92. package/components/SortableTable/selection.js +2 -1
  93. package/components/SortableTable/sorting.js +24 -7
  94. package/components/StatusTable.vue +5 -1
  95. package/components/Tabbed/index.vue +18 -1
  96. package/components/TableDataUserIcon.vue +47 -0
  97. package/components/TypeDescription.vue +1 -0
  98. package/components/Wizard.vue +1 -0
  99. package/components/YamlEditor.vue +1 -0
  100. package/components/__tests__/AppModal.test.ts +98 -0
  101. package/components/__tests__/AsyncButton.test.ts +1 -3
  102. package/components/__tests__/BackLink.test.ts +1 -1
  103. package/components/__tests__/ButtonGroup.test.ts +3 -6
  104. package/components/__tests__/Carousel.test.ts +43 -0
  105. package/components/__tests__/Certificates.test.ts +29 -0
  106. package/components/__tests__/CodeMirror.test.ts +87 -0
  107. package/components/__tests__/CopyCode.test.ts +5 -4
  108. package/components/__tests__/CruResource.test.ts +10 -9
  109. package/components/__tests__/EtcdInfoBanner.test.ts +37 -0
  110. package/components/__tests__/FixedBanner.test.ts +5 -20
  111. package/components/__tests__/NamespaceFilter.test.ts +9 -18
  112. package/components/__tests__/TabTitle.test.ts +129 -0
  113. package/components/auth/AzureWarning.vue +2 -2
  114. package/components/auth/RoleDetailEdit.vue +10 -0
  115. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  116. package/components/auth/login/oidc.vue +7 -1
  117. package/components/fleet/FleetBundles.vue +5 -11
  118. package/components/fleet/FleetClusters.vue +9 -9
  119. package/components/fleet/FleetIntro.vue +11 -17
  120. package/components/fleet/FleetNoWorkspaces.vue +2 -2
  121. package/components/fleet/FleetRepos.vue +63 -27
  122. package/components/fleet/FleetResources.vue +6 -1
  123. package/components/fleet/FleetStatus.vue +3 -3
  124. package/components/fleet/FleetSummary.vue +35 -30
  125. package/components/fleet/ForceDirectedTreeChart/index.vue +9 -3
  126. package/components/fleet/__tests__/FleetSummary.test.ts +316 -0
  127. package/components/form/ArrayList.vue +22 -18
  128. package/components/form/ArrayListSelect.vue +5 -0
  129. package/components/form/BannerSettings.vue +3 -0
  130. package/components/form/ClusterAppearance.vue +132 -0
  131. package/components/form/ColorInput.vue +1 -0
  132. package/components/form/Error.vue +3 -3
  133. package/components/form/FileSelector.vue +1 -0
  134. package/components/form/Footer.vue +2 -2
  135. package/components/form/GitPicker.vue +83 -38
  136. package/components/form/KeyValue.vue +69 -48
  137. package/components/form/LabeledSelect.vue +145 -41
  138. package/components/form/Labels.vue +3 -1
  139. package/components/form/NameNsDescription.vue +26 -9
  140. package/components/form/Password.vue +3 -1
  141. package/components/form/ResourceLabeledSelect.vue +187 -0
  142. package/components/form/ResourceTabs/index.vue +31 -15
  143. package/components/form/SecretSelector.vue +93 -18
  144. package/components/form/SelectOrCreateAuthSecret.vue +132 -59
  145. package/components/form/SimpleSecretSelector.vue +88 -28
  146. package/components/form/__tests__/BannerSettings.test.ts +53 -0
  147. package/components/form/__tests__/KeyValue.test.ts +120 -11
  148. package/components/form/__tests__/LabeledSelect.test.ts +0 -18
  149. package/components/form/__tests__/{NameNsDescription.ts → NameNsDescription.test.ts} +25 -15
  150. package/components/form/labeled-select-utils/labeled-select-pagination.ts +151 -0
  151. package/components/form/labeled-select-utils/labeled-select.utils.ts +122 -0
  152. package/components/formatter/AppSummaryGraph.vue +2 -2
  153. package/components/formatter/Checked.vue +11 -3
  154. package/components/formatter/CloudCredPublicData.vue +30 -0
  155. package/components/formatter/ClusterLink.vue +2 -2
  156. package/components/formatter/ClusterProvider.vue +1 -18
  157. package/components/formatter/FleetClusterSummaryGraph.vue +27 -0
  158. package/components/formatter/FleetSummaryGraph.vue +25 -12
  159. package/components/formatter/ImagePercentageBar.vue +0 -4
  160. package/components/formatter/IngressTarget.vue +18 -7
  161. package/components/formatter/Link.vue +2 -2
  162. package/components/formatter/LinkDetail.vue +2 -2
  163. package/components/formatter/LinkDetailImage.vue +2 -2
  164. package/components/formatter/LinkName.vue +2 -2
  165. package/components/formatter/LiveDuration.vue +1 -1
  166. package/components/formatter/PercentageBar.vue +1 -1
  167. package/components/formatter/PrincipalGroupBindings.vue +2 -2
  168. package/components/formatter/SecretType.vue +2 -2
  169. package/components/formatter/VirtualServiceGateways.vue +2 -2
  170. package/components/formatter/WorkloadDetailEndpoints.vue +12 -22
  171. package/components/formatter/__tests__/Checked.test.ts +19 -0
  172. package/components/formatter/__tests__/LinkDetail.test.ts +5 -5
  173. package/components/formatter/__tests__/WorkloadDetailEndpoints.test.ts +81 -0
  174. package/components/nav/Group.vue +9 -7
  175. package/components/nav/Header.vue +84 -45
  176. package/components/nav/Jump.vue +19 -9
  177. package/components/nav/NamespaceFilter.vue +8 -1
  178. package/components/nav/TopLevelMenu.vue +391 -132
  179. package/components/nav/Type.vue +71 -106
  180. package/components/nav/WindowManager/ContainerLogs.vue +120 -19
  181. package/components/nav/WindowManager/ContainerShell.vue +6 -1
  182. package/components/nav/WindowManager/__tests__/ContainerLogs.test.ts +186 -0
  183. package/components/nav/WindowManager/index.vue +11 -10
  184. package/components/nav/__tests__/TopLevelMenu.test.ts +400 -6
  185. package/components/nav/__tests__/Type.test.ts +322 -97
  186. package/components/nuxt/nuxt-child.js +9 -78
  187. package/components/nuxt/nuxt-error.vue +1 -1
  188. package/components/nuxt/nuxt-link.client.js +13 -95
  189. package/{layouts → components/templates}/blank.vue +1 -1
  190. package/{layouts → components/templates}/default.vue +11 -101
  191. package/{layouts → components/templates}/error.vue +13 -26
  192. package/{layouts → components/templates}/home.vue +4 -1
  193. package/{layouts → components/templates}/plain.vue +4 -1
  194. package/{layouts → components/templates}/standalone.vue +1 -5
  195. package/{layouts → components/templates}/unauthenticated.vue +2 -3
  196. package/composables/useCompactInput.test.ts +36 -0
  197. package/composables/useCompactInput.ts +20 -0
  198. package/composables/useLabeledFormElement.test.ts +135 -0
  199. package/composables/useLabeledFormElement.ts +138 -0
  200. package/config/harvester-manager-types.js +2 -0
  201. package/config/home-links.js +2 -1
  202. package/config/labels-annotations.js +2 -1
  203. package/config/middleware.js +0 -6
  204. package/config/pagination-table-headers.js +57 -0
  205. package/config/pod-security-admission.ts +1 -1
  206. package/config/private-label.js +21 -1
  207. package/config/product/auth.js +1 -0
  208. package/config/product/explorer.js +166 -45
  209. package/config/product/fleet.js +6 -1
  210. package/config/product/legacy.js +2 -11
  211. package/config/product/manager.js +51 -25
  212. package/config/query-params.js +2 -0
  213. package/config/roles.ts +23 -0
  214. package/config/router/index.js +23 -0
  215. package/config/router/navigation-guards/attempt-first-login.js +73 -0
  216. package/config/router/navigation-guards/authentication.js +63 -0
  217. package/config/router/navigation-guards/index.js +15 -0
  218. package/config/router/navigation-guards/load-initial-settings.js +15 -0
  219. package/config/router/routes.js +487 -0
  220. package/config/settings.ts +31 -2
  221. package/config/store.js +8 -4
  222. package/config/system-namespaces.js +3 -0
  223. package/config/table-headers.js +66 -1
  224. package/config/types.js +35 -20
  225. package/config/uiplugins.js +10 -5
  226. package/core/plugin-helpers.js +4 -6
  227. package/core/plugin-routes.ts +56 -114
  228. package/core/plugin.ts +18 -11
  229. package/core/plugins-loader.js +7 -9
  230. package/core/plugins.js +289 -285
  231. package/core/types-provisioning.ts +7 -0
  232. package/creators/app/files/.eslintignore +0 -2
  233. package/creators/app/files/.gitlab-ci.yml +14 -0
  234. package/creators/app/files/.vscode/settings.json +0 -1
  235. package/creators/app/init +19 -0
  236. package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +2 -6
  237. package/creators/pkg/files/.github/workflows/build-extension-charts.yml +2 -6
  238. package/creators/pkg/init +32 -0
  239. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +77 -0
  240. package/detail/__tests__/service.test.ts +62 -0
  241. package/detail/catalog.cattle.io.app.vue +1 -1
  242. package/detail/cis.cattle.io.clusterscan.vue +14 -3
  243. package/detail/fleet.cattle.io.bundle.vue +1 -1
  244. package/detail/fleet.cattle.io.cluster.vue +11 -1
  245. package/detail/fleet.cattle.io.gitrepo.vue +15 -9
  246. package/detail/namespace.vue +2 -2
  247. package/detail/networking.k8s.io.ingress.vue +52 -19
  248. package/detail/node.vue +22 -3
  249. package/detail/provisioning.cattle.io.cluster.vue +27 -9
  250. package/detail/service.vue +1 -1
  251. package/detail/workload/index.vue +1 -0
  252. package/dialog/AddCustomBadgeDialog.vue +318 -161
  253. package/dialog/DeactivateDriverDialog.vue +118 -0
  254. package/dialog/RollbackWorkloadDialog.vue +2 -2
  255. package/dialog/RotateCertificatesDialog.vue +0 -21
  256. package/dialog/ScaleMachineDownDialog.vue +34 -17
  257. package/directives/clean-html.js +15 -0
  258. package/directives/clean-tooltip.js +32 -0
  259. package/directives/focus.js +41 -0
  260. package/directives/int-number.js +21 -0
  261. package/directives/positive-int-number.js +19 -0
  262. package/directives/trim-whitespace.js +19 -0
  263. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +3 -2
  264. package/edit/__tests__/kontainerDriver.test.ts +107 -0
  265. package/edit/__tests__/management.cattle.io.clusterroletemplatebinding.test.ts +12 -1
  266. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  267. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +2 -3
  268. package/edit/__tests__/nodeDriver.test.ts +107 -0
  269. package/edit/__tests__/service.test.ts +85 -0
  270. package/edit/__tests__/ui.cattle.io.navlink.test.ts +3 -1
  271. package/edit/auth/AuthProviderWarningBanners.vue +34 -0
  272. package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +19 -0
  273. package/edit/auth/__tests__/azuread.test.ts +241 -0
  274. package/edit/auth/__tests__/oidc.test.ts +137 -0
  275. package/edit/auth/azuread.vue +133 -31
  276. package/edit/auth/github.vue +5 -17
  277. package/edit/auth/googleoauth.vue +6 -23
  278. package/edit/auth/ldap/index.vue +5 -17
  279. package/edit/auth/oidc.vue +143 -42
  280. package/edit/auth/saml.vue +5 -14
  281. package/edit/catalog.cattle.io.clusterrepo.vue +177 -9
  282. package/edit/cis.cattle.io.clusterscan.vue +5 -2
  283. package/edit/cis.cattle.io.clusterscanbenchmark.vue +41 -9
  284. package/edit/cloudcredential.vue +28 -4
  285. package/edit/configmap.vue +10 -4
  286. package/edit/fleet.cattle.io.gitrepo.vue +3 -1
  287. package/edit/helm.cattle.io.projecthelmchart.vue +29 -19
  288. package/edit/kontainerDriver.vue +65 -0
  289. package/edit/logging-flow/Match.vue +10 -9
  290. package/edit/logging-flow/index.vue +4 -19
  291. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +232 -2
  292. package/edit/logging.banzaicloud.io.output/index.vue +43 -26
  293. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +3 -3
  294. package/edit/management.cattle.io.project.vue +3 -53
  295. package/edit/management.cattle.io.setting.vue +52 -2
  296. package/edit/management.cattle.io.user.vue +2 -1
  297. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +10 -7
  298. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +21 -16
  299. package/edit/monitoring.coreos.com.alertmanagerconfig/types/opsgenie.vue +1 -1
  300. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -2
  301. package/edit/monitoring.coreos.com.alertmanagerconfig/types/slack.vue +1 -1
  302. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +15 -3
  303. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +4 -1
  304. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +2 -0
  305. package/edit/monitoring.coreos.com.prometheusrule/index.vue +2 -0
  306. package/edit/networking.k8s.io.ingress/Rules.vue +8 -3
  307. package/edit/networking.k8s.io.ingress/index.vue +64 -8
  308. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +1 -0
  309. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +6 -2
  310. package/edit/networking.k8s.io.networkpolicy/__tests__/{PolicyRuleTarget.spec.ts → PolicyRuleTarget.test.ts} +46 -7
  311. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/{selectors.ts → selectors.test.ts} +1 -1
  312. package/edit/networking.k8s.io.networkpolicy/index.vue +2 -0
  313. package/edit/nodeDriver.vue +65 -0
  314. package/edit/persistentvolume/index.vue +2 -2
  315. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +33 -16
  316. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +276 -0
  317. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +473 -0
  318. package/edit/provisioning.cattle.io.cluster/__tests__/{CustomCommand.tests.ts → CustomCommand.test.ts} +3 -0
  319. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +228 -0
  320. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +1 -1
  321. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +73 -0
  322. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +77 -13
  323. package/edit/provisioning.cattle.io.cluster/__tests__/utils/cluster.ts +391 -0
  324. package/edit/provisioning.cattle.io.cluster/import.vue +4 -4
  325. package/edit/provisioning.cattle.io.cluster/index.vue +126 -51
  326. package/edit/provisioning.cattle.io.cluster/rke2.vue +325 -791
  327. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +137 -0
  328. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +217 -0
  329. package/edit/provisioning.cattle.io.cluster/{Basics.vue → tabs/Basics.vue} +123 -129
  330. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +132 -0
  331. package/edit/provisioning.cattle.io.cluster/{MachinePool.vue → tabs/MachinePool.vue} +1 -0
  332. package/edit/provisioning.cattle.io.cluster/{S3Config.vue → tabs/etcd/S3Config.vue} +1 -0
  333. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +135 -0
  334. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +190 -0
  335. package/edit/provisioning.cattle.io.cluster/{RegistryConfigs.vue → tabs/registries/RegistryConfigs.vue} +3 -0
  336. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +148 -0
  337. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/index.vue +76 -0
  338. package/edit/resources.cattle.io.backup.vue +139 -124
  339. package/edit/resources.cattle.io.restore.vue +146 -126
  340. package/edit/service.vue +13 -0
  341. package/edit/serviceaccount.vue +46 -4
  342. package/edit/token.vue +1 -0
  343. package/edit/workload/Upgrading.vue +3 -2
  344. package/edit/workload/__tests__/Job.test.ts +1 -3
  345. package/edit/workload/__tests__/Upgrading.test.ts +2 -2
  346. package/edit/workload/index.vue +2 -1
  347. package/edit/workload/mixins/workload.js +35 -2
  348. package/edit/workload/storage/emptyDir.vue +2 -2
  349. package/initialize/App.vue +75 -0
  350. package/initialize/app-extended.js +128 -0
  351. package/initialize/entry-helpers.js +549 -0
  352. package/initialize/entry.js +32 -0
  353. package/initialize/install-components.js +23 -0
  354. package/initialize/install-directives.js +59 -0
  355. package/initialize/install-plugins.js +123 -0
  356. package/list/__tests__/workload.test.ts +1 -1
  357. package/list/catalog.cattle.io.app.vue +1 -0
  358. package/list/cis.cattle.io.clusterscan.vue +16 -10
  359. package/list/group.principal.vue +2 -2
  360. package/list/management.cattle.io.feature.vue +12 -14
  361. package/list/management.cattle.io.setting.vue +1 -0
  362. package/list/management.cattle.io.user.vue +13 -4
  363. package/list/networking.k8s.io.ingress.vue +36 -0
  364. package/list/node.vue +212 -73
  365. package/list/provisioning.cattle.io.cluster.vue +17 -4
  366. package/list/ui.cattle.io.navlink.vue +2 -2
  367. package/list/workload.vue +22 -0
  368. package/machine-config/__tests__/vmwarevsphere-pool-config-merge.test.ts +30 -0
  369. package/machine-config/__tests__/vmwarevsphere.test.ts +1 -3
  370. package/machine-config/amazonec2.vue +1 -1
  371. package/machine-config/azure.vue +2 -1
  372. package/machine-config/generic.vue +11 -15
  373. package/machine-config/vmwarevsphere-pool-config-merge.ts +25 -0
  374. package/machine-config/vmwarevsphere.vue +31 -27
  375. package/middleware/authenticated.js +23 -380
  376. package/mixins/__tests__/chart.test.ts +48 -6
  377. package/mixins/__tests__/create-edit-view.test.ts +2 -3
  378. package/mixins/auth-config.js +5 -9
  379. package/mixins/brand.js +102 -96
  380. package/mixins/chart.js +27 -13
  381. package/mixins/create-edit-view/index.js +2 -2
  382. package/mixins/fetch.client.js +42 -48
  383. package/mixins/labeled-form-element.ts +27 -2
  384. package/mixins/page-actions.js +7 -5
  385. package/mixins/resource-fetch-api-pagination.js +304 -0
  386. package/mixins/resource-fetch-namespaced.js +1 -1
  387. package/mixins/resource-fetch.js +46 -5
  388. package/models/__tests__/cluster.test.ts +44 -0
  389. package/models/__tests__/fleet.cattle.io.cluster.test.ts +36 -0
  390. package/models/__tests__/management.cattle.io.cluster.test.ts +23 -0
  391. package/models/__tests__/management.cattle.io.node.ts +85 -0
  392. package/models/__tests__/management.cattle.io.nodepool.ts +83 -0
  393. package/models/__tests__/namespace.test.ts +49 -9
  394. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +241 -0
  395. package/models/__tests__/schema.tests.ts +24 -0
  396. package/models/__tests__/secret.test.ts +37 -0
  397. package/models/__tests__/steve-schema.test.ts +73 -0
  398. package/models/__tests__/storage.k8s.io.storageclass.test.ts +22 -0
  399. package/models/__tests__/workload.test.ts +91 -0
  400. package/models/catalog.cattle.io.app.js +8 -0
  401. package/models/catalog.cattle.io.clusterrepo.js +9 -1
  402. package/models/catalog.cattle.io.uiplugin.js +7 -8
  403. package/models/cis.cattle.io.clusterscan.js +29 -8
  404. package/models/cloudcredential.js +9 -1
  405. package/models/cluster/node.js +8 -4
  406. package/models/cluster/schema.js +6 -0
  407. package/models/cluster.js +33 -0
  408. package/models/cluster.x-k8s.io.machine.js +1 -1
  409. package/models/cluster.x-k8s.io.machinedeployment.js +14 -0
  410. package/models/driver.js +62 -0
  411. package/models/fleet.cattle.io.cluster.js +27 -11
  412. package/models/fleet.cattle.io.gitrepo.js +66 -13
  413. package/models/helm.cattle.io.projecthelmchart.js +1 -1
  414. package/models/kontainerdriver.js +68 -0
  415. package/models/management/schema.js +6 -0
  416. package/models/management.cattle.io.authconfig.js +3 -2
  417. package/models/management.cattle.io.cluster.js +16 -7
  418. package/models/management.cattle.io.globalrole.js +2 -0
  419. package/models/management.cattle.io.kontainerdriver.js +1 -0
  420. package/models/management.cattle.io.node.js +18 -14
  421. package/models/management.cattle.io.nodepool.js +17 -0
  422. package/models/management.cattle.io.project.js +0 -36
  423. package/models/management.cattle.io.setting.js +11 -7
  424. package/models/management.cattle.io.user.js +2 -2
  425. package/models/monitoring.coreos.com.receiver.js +3 -1
  426. package/models/monitoring.coreos.com.route.js +1 -1
  427. package/models/namespace.js +1 -1
  428. package/models/networking.k8s.io.ingress.js +2 -1
  429. package/models/nodedriver.js +68 -0
  430. package/models/pod.js +20 -0
  431. package/models/provisioning.cattle.io.cluster.js +125 -10
  432. package/models/schema.js +28 -7
  433. package/models/secret.js +126 -18
  434. package/models/service.js +2 -0
  435. package/models/steve-schema.ts +254 -0
  436. package/models/storage.k8s.io.storageclass.js +1 -1
  437. package/models/workload.js +17 -0
  438. package/models/workload.service.js +18 -0
  439. package/package.json +17 -14
  440. package/pages/about.vue +12 -6
  441. package/pages/account/create-key.vue +0 -1
  442. package/pages/account/index.vue +7 -3
  443. package/pages/auth/login.vue +106 -103
  444. package/pages/auth/logout.vue +2 -4
  445. package/pages/auth/setup.vue +92 -66
  446. package/pages/auth/verify.vue +30 -24
  447. package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +2 -17
  448. package/pages/c/_cluster/apps/charts/chart.vue +54 -9
  449. package/pages/c/_cluster/apps/charts/index.vue +99 -69
  450. package/pages/c/_cluster/apps/charts/install.helpers.js +2 -13
  451. package/pages/c/_cluster/apps/charts/install.vue +9 -9
  452. package/pages/c/_cluster/auth/config/_id.vue +0 -6
  453. package/pages/c/_cluster/auth/config/index.vue +15 -9
  454. package/pages/c/_cluster/auth/roles/index.vue +8 -10
  455. package/pages/c/_cluster/ecm/index.vue +0 -2
  456. package/pages/c/_cluster/explorer/ConfigBadge.vue +13 -8
  457. package/pages/c/_cluster/explorer/EventsTable.vue +18 -0
  458. package/pages/c/_cluster/explorer/__tests__/index.test.ts +181 -0
  459. package/pages/c/_cluster/explorer/index.vue +253 -91
  460. package/pages/c/_cluster/explorer/tools/__tests__/index.test.ts +69 -0
  461. package/pages/c/_cluster/explorer/tools/index.vue +10 -6
  462. package/pages/c/_cluster/fleet/index.vue +89 -94
  463. package/pages/c/_cluster/longhorn/__tests__/longhorn.index.test.ts +89 -0
  464. package/pages/c/_cluster/longhorn/index.vue +52 -17
  465. package/pages/c/_cluster/manager/cloudCredential/index.vue +18 -25
  466. package/pages/c/_cluster/manager/drivers/kontainerDriver/_id.vue +12 -0
  467. package/pages/c/_cluster/manager/drivers/kontainerDriver/create.vue +15 -0
  468. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +94 -0
  469. package/pages/c/_cluster/manager/drivers/nodeDriver/_id.vue +12 -0
  470. package/pages/c/_cluster/manager/drivers/nodeDriver/create.vue +15 -0
  471. package/pages/c/_cluster/manager/drivers/nodeDriver/index.vue +63 -0
  472. package/pages/c/_cluster/manager/jwt.authentication/index.vue +235 -0
  473. package/pages/c/_cluster/manager/pages/_page.vue +4 -5
  474. package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +4 -0
  475. package/pages/c/_cluster/monitoring/route-receiver/index.vue +2 -2
  476. package/pages/c/_cluster/neuvector/index.vue +1 -0
  477. package/pages/c/_cluster/settings/DefaultLinksEditor.vue +1 -0
  478. package/pages/c/_cluster/settings/banners.vue +86 -10
  479. package/pages/c/_cluster/settings/brand.vue +261 -38
  480. package/pages/c/_cluster/settings/index.vue +4 -6
  481. package/pages/c/_cluster/settings/links.vue +3 -2
  482. package/pages/c/_cluster/settings/performance.vue +71 -3
  483. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +5 -2
  484. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +12 -8
  485. package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +9 -6
  486. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +10 -46
  487. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +11 -5
  488. package/pages/c/_cluster/uiplugins/InstallDialog.vue +53 -18
  489. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +36 -301
  490. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +14 -6
  491. package/pages/c/_cluster/uiplugins/__tests__/SetupUIPlugins.test.ts +52 -106
  492. package/pages/c/_cluster/uiplugins/index.vue +38 -54
  493. package/pages/diagnostic.vue +2 -2
  494. package/pages/fail-whale.vue +103 -42
  495. package/pages/home.vue +81 -24
  496. package/pages/prefs.vue +8 -4
  497. package/pages/support/index.vue +14 -10
  498. package/pkg/auto-import.js +1 -1
  499. package/plugins/axios.js +0 -36
  500. package/plugins/back-button.js +3 -5
  501. package/plugins/clean-html-directive.js +5 -30
  502. package/plugins/clean-html.js +53 -0
  503. package/plugins/clean-tooltip-directive.js +6 -31
  504. package/plugins/codemirror-loader.js +1 -1
  505. package/plugins/codemirror.js +41 -9
  506. package/plugins/dashboard-store/__tests__/mutations.test.ts +389 -0
  507. package/plugins/dashboard-store/__tests__/resource-class.test.ts +49 -0
  508. package/plugins/dashboard-store/__tests__/utils/store-mocks.ts +7 -0
  509. package/plugins/dashboard-store/actions.js +132 -25
  510. package/plugins/dashboard-store/classify.js +1 -18
  511. package/plugins/dashboard-store/getters.js +154 -44
  512. package/plugins/dashboard-store/index.js +0 -111
  513. package/plugins/dashboard-store/mutations.js +150 -52
  514. package/plugins/dashboard-store/resource-class.js +77 -127
  515. package/plugins/directives.js +6 -39
  516. package/plugins/ember-cookie.js +13 -0
  517. package/plugins/global-formatters.js +26 -5
  518. package/plugins/i18n.js +89 -55
  519. package/plugins/int-number.js +6 -20
  520. package/plugins/plugin.js +3 -3
  521. package/plugins/positive-int-number.js +6 -17
  522. package/plugins/steve/__tests__/{getters.spec.ts → getters.test.ts} +81 -10
  523. package/plugins/steve/__tests__/mutations.test.ts +49 -0
  524. package/plugins/steve/__tests__/resource-utils.test.ts +159 -0
  525. package/plugins/steve/__tests__/steve-class.test.ts +59 -0
  526. package/plugins/steve/__tests__/subscribe.spec.ts +4 -1
  527. package/plugins/steve/__tests__/utils/mutation.test.helpers.ts +105 -0
  528. package/plugins/steve/__tests__/utils/steve-mocks.ts +31 -0
  529. package/plugins/steve/accept-or-reject-socket-message.ts +103 -0
  530. package/plugins/steve/actions.js +3 -38
  531. package/plugins/steve/getters.js +164 -61
  532. package/plugins/steve/hybrid-class.js +5 -1
  533. package/plugins/steve/mutations.js +24 -3
  534. package/plugins/steve/norman-class.js +142 -2
  535. package/{utils → plugins/steve}/projectAndNamespaceFiltering.utils.ts +28 -10
  536. package/plugins/steve/resource-utils.ts +38 -0
  537. package/plugins/steve/schema.d.ts +22 -0
  538. package/plugins/steve/steve-class.js +22 -0
  539. package/plugins/steve/steve-pagination-utils.ts +368 -0
  540. package/plugins/steve/subscribe.js +17 -76
  541. package/plugins/trim-whitespace.js +6 -34
  542. package/plugins/vue-js-modal.js +1 -1
  543. package/public/index.html +1 -0
  544. package/rancher-components/Form/LabeledInput/LabeledInput.vue +13 -8
  545. package/rancher-components/components/BadgeState/BadgeState.vue +3 -3
  546. package/rancher-components/components/Banner/Banner.test.ts +1 -5
  547. package/rancher-components/components/Banner/Banner.vue +2 -2
  548. package/rancher-components/components/Card/Card.vue +4 -4
  549. package/rancher-components/components/Form/Checkbox/Checkbox.vue +4 -3
  550. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  551. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +57 -24
  552. package/rancher-components/components/Form/Radio/RadioButton.test.ts +1 -3
  553. package/rancher-components/components/Form/Radio/RadioButton.vue +13 -7
  554. package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -3
  555. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +6 -4
  556. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +7 -4
  557. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +9 -4
  558. package/rancher-components/components/StringList/StringList.test.ts +270 -0
  559. package/rancher-components/components/StringList/StringList.vue +65 -26
  560. package/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml +69 -0
  561. package/scripts/clean +1 -1
  562. package/scripts/extension/bundle +19 -7
  563. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +11 -0
  564. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -0
  565. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
  566. package/scripts/extension/helm/package/Dockerfile +1 -1
  567. package/scripts/extension/helm/scripts/package +11 -3
  568. package/scripts/extension/helm/scripts/patch +27 -0
  569. package/scripts/extension/parse-tag-name +4 -4
  570. package/scripts/extension/publish +25 -14
  571. package/scripts/publish-shell.sh +11 -1
  572. package/scripts/serve-pkgs +0 -2
  573. package/scripts/test-plugins-build.sh +87 -11
  574. package/scripts/vue-migrate.js +683 -0
  575. package/server/har-file.js +183 -0
  576. package/store/__tests__/catalog.test.ts +224 -0
  577. package/store/__tests__/type-map.test.ts +1122 -0
  578. package/store/auth.js +23 -4
  579. package/store/aws.js +53 -6
  580. package/store/catalog.js +22 -6
  581. package/store/cru-resource.ts +26 -0
  582. package/store/customisation.js +35 -0
  583. package/store/features.js +7 -4
  584. package/store/i18n.js +11 -0
  585. package/store/index.js +139 -48
  586. package/store/plugins.js +8 -4
  587. package/store/prefs.js +33 -38
  588. package/store/type-map.js +288 -213
  589. package/store/type-map.utils.ts +226 -0
  590. package/tsconfig.json +34 -9
  591. package/tsconfig.paths.json +21 -0
  592. package/types/components/labeledSelect.ts +50 -0
  593. package/types/resources/settings.d.ts +32 -0
  594. package/types/{userPreferences.d.ts → resources/userPreferences.d.ts} +0 -1
  595. package/types/shell/index.d.ts +1042 -767
  596. package/types/store/dashboard-store.types.ts +42 -0
  597. package/types/store/pagination.types.ts +457 -0
  598. package/types/store/type-map.ts +30 -0
  599. package/types/store/vuex.d.ts +9 -0
  600. package/types/vue-shim.d.ts +51 -0
  601. package/utils/__tests__/cluster.test.ts +20 -18
  602. package/utils/__tests__/create-yaml.test.ts +359 -2
  603. package/utils/__tests__/kontainer.test.ts +92 -0
  604. package/utils/__tests__/pod-security-admission.test.ts +1 -1
  605. package/utils/alertmanagerconfig.js +19 -0
  606. package/utils/array.ts +40 -1
  607. package/utils/async.ts +2 -0
  608. package/utils/auth.js +152 -4
  609. package/utils/axios.js +2 -21
  610. package/utils/azure.js +24 -0
  611. package/utils/banners.js +103 -0
  612. package/utils/clipboard.js +5 -0
  613. package/utils/cluster.js +1 -1
  614. package/utils/config.js +4 -0
  615. package/utils/create-yaml.js +59 -28
  616. package/utils/custom-validators.js +0 -2
  617. package/utils/error.js +41 -1
  618. package/utils/formatter.js +5 -3
  619. package/utils/git.ts +1 -1
  620. package/utils/install-redirect.js +1 -1
  621. package/utils/kontainer.ts +186 -0
  622. package/utils/object.js +24 -0
  623. package/utils/pagination-utils.ts +154 -0
  624. package/utils/pod-security-admission.ts +1 -1
  625. package/utils/router.js +86 -0
  626. package/utils/settings.ts +46 -0
  627. package/utils/time.js +2 -1
  628. package/utils/unit-tests/ChildRenderingRouterLinkStub.ts +36 -0
  629. package/utils/url.ts +1 -1
  630. package/utils/validators/formRules/__tests__/index.test.ts +70 -4
  631. package/utils/validators/formRules/index.ts +15 -9
  632. package/utils/validators/index.js +1 -0
  633. package/utils/validators/setting.js +6 -10
  634. package/vue.config.js +377 -401
  635. package/.DS_Store +0 -0
  636. package/assets/images/providers/aks-black.svg +0 -28
  637. package/assets/images/providers/aks.svg +0 -31
  638. package/assets/styles/vendor/vue-js-modal.scss +0 -16
  639. package/components/ChartPsp.vue +0 -76
  640. package/components/EventsTable.vue +0 -67
  641. package/components/TabbedLinks/index.vue +0 -94
  642. package/components/__tests__/ChartPsp.test.ts +0 -75
  643. package/components/formatter/__tests__/ClusterProvider.test.ts +0 -28
  644. package/components/nuxt/nuxt-link.server.js +0 -16
  645. package/components/nuxt/nuxt.js +0 -101
  646. package/config/router.js +0 -408
  647. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -237
  648. package/initialize/App.js +0 -198
  649. package/initialize/client.js +0 -875
  650. package/initialize/index.js +0 -364
  651. package/initialize/layouts.ts +0 -26
  652. package/middleware/i18n.js +0 -10
  653. package/middleware/unauthenticated.js +0 -22
  654. package/mixins/fetch.server.js +0 -73
  655. package/pages/c/_cluster/apps/index.vue +0 -17
  656. package/pages/c/_cluster/auth/index.vue +0 -19
  657. package/pages/c/_cluster/index.vue +0 -17
  658. package/pages/c/_cluster/legacy/index.vue +0 -22
  659. package/pages/c/_cluster/manager/index.vue +0 -22
  660. package/pages/c/_cluster/mcapps/index.vue +0 -21
  661. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +0 -232
  662. package/pages/c/index.vue +0 -9
  663. package/pages/rio/mesh.vue +0 -508
  664. package/plugins/dashboard-store/__tests__/mutations.spec.ts +0 -406
  665. package/plugins/dashboard-store/rehydrate-all.js +0 -44
  666. package/plugins/portal-vue.js +0 -4
  667. package/plugins/portal.js +0 -4
  668. package/plugins/resize.js +0 -5
  669. package/plugins/shortkey.js +0 -4
  670. package/plugins/tooltip.js +0 -4
  671. package/plugins/transitions.js +0 -4
  672. package/plugins/v-select.js +0 -4
  673. package/plugins/vue-clipboard2.js +0 -4
  674. package/rancher-components/components/BadgeState/BadgeState.spec.ts +0 -12
  675. package/tsconfig.default.json +0 -46
  676. package/utils/group.js +0 -70
  677. package/utils/nuxt.js +0 -659
  678. package/utils/router.scrollBehavior.js +0 -80
  679. /package/components/__tests__/{Collapse.spec.ts → Collapse.test.ts} +0 -0
  680. /package/edit/provisioning.cattle.io.cluster/{AgentConfiguration.vue → tabs/AgentConfiguration.vue} +0 -0
  681. /package/edit/provisioning.cattle.io.cluster/{MemberRoles.vue → tabs/MemberRoles.vue} +0 -0
  682. /package/edit/provisioning.cattle.io.cluster/{ACE.vue → tabs/networking/ACE.vue} +0 -0
  683. /package/edit/provisioning.cattle.io.cluster/{RegistryMirrors.vue → tabs/registries/RegistryMirrors.vue} +0 -0
  684. /package/edit/provisioning.cattle.io.cluster/{DrainOptions.vue → tabs/upgrade/DrainOptions.vue} +0 -0
  685. /package/models/__tests__/{node.ts → node.test.ts} +0 -0
  686. /package/plugins/dashboard-store/__tests__/{actions.spec.ts → actions.test.ts} +0 -0
  687. /package/plugins/dashboard-store/__tests__/{getters.spec.ts → getters.test.ts} +0 -0
  688. /package/plugins/steve/__tests__/{header-warnings.spec.ts → header-warnings.test.ts} +0 -0
  689. /package/types/{pod-security-admission.ts → resources/pod-security-admission.ts} +0 -0
@@ -0,0 +1,186 @@
1
+ import { isArray } from '@shell/utils/array';
2
+ import { set, get, isEmpty } from '@shell/utils/object';
3
+
4
+ /**
5
+ * This function accepts a v3 cluster object and mutates its config field to sync values that were set outside Rancher (eg, when an imported cluster is created in its respective cloud provider).
6
+ * Values configured outside of rancher are not automatically propagated to the config field that the UI edits; they are reflected in the aksStatus/eksStatus/gkeStatus.upstreamSpec field.
7
+ * This function works in tandem with diffUpstreamSpec: the former runs when the edit form is initialized and the latter runs when the cluster is saved.
8
+ * @param configPrefix one of aks, eks, gke
9
+ * @param normanCluster v3 cluster object
10
+ */
11
+ export function syncUpstreamConfig(configPrefix: string, normanCluster: {[key: string]: any}): void {
12
+ const configKey = `${ configPrefix }Config`;
13
+ const statusKey = `${ configPrefix }Status`;
14
+
15
+ const rancherConfig = normanCluster[configKey] || {};
16
+ const upstreamConfig = normanCluster?.[statusKey]?.upstreamSpec || {};
17
+
18
+ if (!isEmpty(upstreamConfig)) {
19
+ Object.keys(upstreamConfig).forEach((key) => {
20
+ if (isEmpty(rancherConfig[key]) && !isEmpty(upstreamConfig[key])) {
21
+ set(rancherConfig, key, upstreamConfig[key]);
22
+ }
23
+ });
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Hosted provider (aks gke eks) edit functionality differs from other k8s resources
29
+ * see ui issue: https://github.com/rancher/rancher/issues/28541
30
+ * backend issue: https://github.com/rancher/rancher/issues/28422
31
+ * The below code is taken from the rancher/ui implementation https://github.com/rancher/ui/blob/master/app/models/cluster.js#L1368
32
+ */
33
+
34
+ function isEmptyObject(object: any) {
35
+ return Object.keys(object).length === 0;
36
+ }
37
+
38
+ function isObject(val: any) {
39
+ return Object.prototype.toString.call(val) === '[object Object]';
40
+ }
41
+
42
+ function isEmptyStringOrArray(val: any) {
43
+ return val === null || val === undefined || val === '' || (isArray(val) && val.length === 0);
44
+ }
45
+
46
+ /**
47
+ * this is NOT a generic object diff.
48
+ * It tries to be as generic as possible but it does make certain assumptions regarding nulls and emtpy arrays/objects
49
+ * if upstream is null and local aks config is empty we do not count this as a change
50
+ * additionally null values on the RHS will be ignored as null cant be sent in this case
51
+ * nodeGroups, nodePools, tags, and labels are not diffed
52
+ * @param upstream aksStatus.upstreamSpec
53
+ * @param local aksConfig
54
+ * @returns aksConfig containing only values which differ from upstream spec, excluding null values
55
+ */
56
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
57
+ export function diffUpstreamSpec(upstream: any, local: any): any {
58
+ const delta = {};
59
+ const localKeys = Object.keys(local);
60
+
61
+ localKeys.forEach((key) => {
62
+ if (key === 'type') {
63
+ return;
64
+ }
65
+
66
+ const upstreamMatch = get(upstream, key);
67
+ const localMatch = get(local, key);
68
+
69
+ if (key !== 'nodeGroups' && key !== 'nodePools') {
70
+ try {
71
+ if (JSON.stringify(upstreamMatch) === JSON.stringify(localMatch)) {
72
+ return;
73
+ }
74
+ } catch (e) {}
75
+ }
76
+
77
+ if (key === 'nodeGroups' || key === 'nodePools' || key === 'tags' || key === 'labels') {
78
+ // Node Groups and Node Pools do not require a sync, we can safely send the entire object
79
+ // Tags and Labels (maps) are also included by default because what is present in the config is exactly what should be used on save and any equal maps would have been caught by the JSON isEqual comparison above
80
+ if (!isEmptyStringOrArray(localMatch)) {
81
+ // node groups need ALL data so short circut and send it all
82
+ set(delta, key, localMatch);
83
+ } else {
84
+ // all node groups were deleted
85
+ set(delta, key, []);
86
+ }
87
+
88
+ return;
89
+ }
90
+
91
+ if (isEmptyStringOrArray(upstreamMatch) || isEmptyObject(upstreamMatch)) {
92
+ if (isEmptyStringOrArray(localMatch) || isEmptyObject(localMatch)) {
93
+ if (upstreamMatch !== null && (isArray(localMatch) || isObject(localMatch))) {
94
+ // Empty Arrays and Empty Maps must be sent as such unless the upstream value is null, then the empty array or obj is just an init value
95
+ set(delta, key, localMatch);
96
+ }
97
+ } else {
98
+ // upstream is empty, local is not, just set
99
+ set(delta, key, localMatch);
100
+ }
101
+ } else {
102
+ if (localMatch !== null) {
103
+ // entry in og obj
104
+ if (isArray(upstreamMatch)) {
105
+ if (isArray(localMatch)) {
106
+ if (!isEmptyStringOrArray(localMatch) && localMatch.every((m: any) => isObject(m))) {
107
+ // You have more diffing to do
108
+ localMatch.forEach((match: any) => {
109
+ // our most likely candiate for a match is node group name, but lets check the others just incase.
110
+ const matchId = get(match, 'name') || get(match, 'id') || false;
111
+
112
+ if (matchId) {
113
+ let lmatchIdx = 0;
114
+
115
+ // we have soime kind of identifier to find a match in the upstream, so we can diff and insert to new array
116
+ const lMatch = upstreamMatch.find((l: any, idx: number) => {
117
+ const lmatchId = get(l, 'name') || get(l, 'id');
118
+
119
+ if (lmatchId === matchId) {
120
+ lmatchIdx = idx;
121
+
122
+ return l;
123
+ }
124
+ });
125
+
126
+ if (lMatch) {
127
+ // we have a match in the upstream, meaning we've probably made updates to the object itself
128
+ const diffedMatch = diffUpstreamSpec(lMatch, match);
129
+
130
+ if (!isArray(get(delta, key))) {
131
+ set(delta, key, [diffedMatch]);
132
+ } else {
133
+ const target: any[] = delta[key as keyof typeof delta];
134
+
135
+ // diff and push into new array
136
+ target.splice(lmatchIdx, 0, diffedMatch);
137
+ }
138
+ } else {
139
+ // no match in upstream, new entry
140
+ if (!isArray(get(delta, key))) {
141
+ set(delta, key, [match]);
142
+ } else {
143
+ const target: any = delta[key as keyof typeof delta];
144
+
145
+ target.push(match);
146
+ }
147
+ }
148
+ } else {
149
+ // no match id, all we can do is dumb add
150
+ if (!isArray(get(delta, key))) {
151
+ set(delta, key, [match]);
152
+ } else {
153
+ const target: any = delta[key as keyof typeof delta];
154
+
155
+ target.push(match);
156
+ }
157
+ }
158
+ });
159
+ } else {
160
+ set(delta, key, localMatch);
161
+ }
162
+ } else {
163
+ set(delta, key, localMatch);
164
+ }
165
+ } else if (isObject(upstreamMatch)) {
166
+ if (!isEmptyStringOrArray(localMatch) && !isEmptyObject(localMatch)) {
167
+ if ((Object.keys(upstreamMatch) || []).length > 0) {
168
+ // You have more diffing to do
169
+ set(delta, key, diffUpstreamSpec(upstreamMatch, localMatch));
170
+ } else if (isEmptyObject(upstreamMatch)) {
171
+ // we had a map now we have an empty map
172
+ set(delta, key, {});
173
+ }
174
+ } else if (!isEmptyObject(upstreamMatch) && isEmptyObject(localMatch)) {
175
+ // we had a map now we have an empty map
176
+ set(delta, key, {});
177
+ }
178
+ } else { // upstreamMatch not an array or object
179
+ set(delta, key, localMatch);
180
+ }
181
+ }
182
+ }
183
+ });
184
+
185
+ return delta;
186
+ }
package/utils/object.js CHANGED
@@ -110,6 +110,20 @@ export function remove(obj, path) {
110
110
  return obj;
111
111
  }
112
112
 
113
+ /**
114
+ * `delete` a property at the given path.
115
+ *
116
+ * This is similar to `remove` but doesn't need any fancy kube obj path splitting
117
+ * and doesn't use `Vue.set` (avoids reactivity)
118
+ */
119
+ export function deleteProperty(obj, path) {
120
+ const pathAr = path.split('.');
121
+ const propToDelete = pathAr.pop();
122
+
123
+ // Walk down path until final prop, then delete final prop
124
+ delete pathAr.reduce((o, k) => o[k] || {}, obj)[propToDelete];
125
+ }
126
+
113
127
  export function getter(path) {
114
128
  return function(obj) {
115
129
  return get(obj, path);
@@ -411,3 +425,13 @@ export function pickBy(obj = {}, predicate = (value, key) => false) {
411
425
  export const toDictionary = (array, callback) => Object.assign(
412
426
  {}, ...array.map((item) => ({ [item]: callback(item) }))
413
427
  );
428
+
429
+ export function dropKeys(obj, keys) {
430
+ if ( !obj ) {
431
+ return;
432
+ }
433
+
434
+ for ( const k of keys ) {
435
+ delete obj[k];
436
+ }
437
+ }
@@ -0,0 +1,154 @@
1
+ import { PaginationSettings } from '@shell/types/resources/settings';
2
+ import {
3
+ NAMESPACE_FILTER_ALL_USER as ALL_USER,
4
+ NAMESPACE_FILTER_ALL as ALL,
5
+ NAMESPACE_FILTER_ALL_SYSTEM as ALL_SYSTEM,
6
+ NAMESPACE_FILTER_NAMESPACED_YES as NAMESPACED_YES,
7
+ NAMESPACE_FILTER_NAMESPACED_NO as NAMESPACED_NO,
8
+ NAMESPACE_FILTER_KINDS,
9
+ NAMESPACE_FILTER_NS_FULL_PREFIX,
10
+ NAMESPACE_FILTER_P_FULL_PREFIX,
11
+ } from '@shell/utils/namespace-filter';
12
+ import { PaginationArgs, PaginationParam, PaginationSort } from '@shell/types/store/pagination.types';
13
+ import { sameArrayObjects } from '@shell/utils/array';
14
+ import { isEqual } from '@shell/utils/object';
15
+ import { STEVE_CACHE } from '@shell/store/features';
16
+ import { getPerformanceSetting } from '@shell/utils/settings';
17
+
18
+ /**
19
+ * Helper functions for server side pagination
20
+ */
21
+ class PaginationUtils {
22
+ /**
23
+ * When a ns filter isn't one or more projects/namespaces... what are the valid values?
24
+ *
25
+ * This basically blocks 'Not in a Project'.. which would involve a projectsornamespaces param with every ns not in a project.
26
+ */
27
+ validNsProjectFilters = [ALL, ALL_SYSTEM, ALL_USER, ALL_SYSTEM, NAMESPACE_FILTER_KINDS.NAMESPACE, NAMESPACE_FILTER_KINDS.PROJECT, NAMESPACED_YES, NAMESPACED_NO];
28
+
29
+ private getSettings({ rootGetters }: any): PaginationSettings {
30
+ const perf = getPerformanceSetting(rootGetters);
31
+
32
+ return perf.serverPagination;
33
+ }
34
+
35
+ isSteveCacheEnabled({ rootGetters }: any): boolean {
36
+ // We always get Feature flags as part of start up (see `dispatch('features/loadServer')` in loadManagement)
37
+ return rootGetters['features/get']?.(STEVE_CACHE);
38
+ }
39
+
40
+ /**
41
+ * Is pagination enabled at a global level or for a specific resource
42
+ */
43
+ isEnabled({ rootGetters }: any, enabledFor: {
44
+ store: string,
45
+ resource?: {
46
+ id: string,
47
+ }
48
+ }) {
49
+ // Cache must be enabled to support pagination api
50
+ if (!this.isSteveCacheEnabled({ rootGetters })) {
51
+ return false;
52
+ }
53
+
54
+ const settings = this.getSettings({ rootGetters });
55
+
56
+ // No setting, not enabled
57
+ if (!settings?.enabled) {
58
+ return false;
59
+ }
60
+
61
+ // Missing required params, not enabled
62
+ if (!enabledFor) {
63
+ return false;
64
+ }
65
+
66
+ const storeSettings = settings.stores?.[enabledFor.store];
67
+
68
+ // No pagination setting for target store, not enabled
69
+ if (!storeSettings) {
70
+ return false;
71
+ }
72
+
73
+ // Not interested in a resource, so just top level settings are checked
74
+ if (!enabledFor.resource) {
75
+ return true;
76
+ }
77
+
78
+ // Store says all resources are enabled
79
+ if (storeSettings.resources.enableAll) {
80
+ return true;
81
+ }
82
+
83
+ // given a resource... but no id... invalid
84
+ if (!enabledFor.resource.id) {
85
+ return false;
86
+ }
87
+
88
+ // Store says only some (those that have pagination columns not from schema and no custom list)
89
+ const isGeneric =
90
+ !rootGetters['type-map/configuredHeaders'](enabledFor.resource.id) &&
91
+ !rootGetters['type-map/configuredPaginationHeaders'](enabledFor.resource.id) &&
92
+ !rootGetters['type-map/hasCustomList'](enabledFor.resource.id);
93
+
94
+ if (storeSettings.resources.enableSome.generic && isGeneric) {
95
+ return true;
96
+ }
97
+
98
+ if (storeSettings.resources.enableSome.enabled.includes(enabledFor.resource.id)) {
99
+ return true;
100
+ }
101
+
102
+ return false;
103
+ }
104
+
105
+ validateNsProjectFilters(nsProjectFilters: string[]) {
106
+ return nsProjectFilters?.every((f) => this.validateNsProjectFilter(f));
107
+ }
108
+
109
+ validateNsProjectFilter(nsProjectFilter: string) {
110
+ if (nsProjectFilter.startsWith(NAMESPACE_FILTER_NS_FULL_PREFIX) || nsProjectFilter.startsWith(NAMESPACE_FILTER_P_FULL_PREFIX)) {
111
+ return true;
112
+ }
113
+
114
+ return this.validNsProjectFilters.includes(nsProjectFilter);
115
+ }
116
+
117
+ paginationFilterEqual(a: PaginationParam, b: PaginationParam): boolean {
118
+ if (a.param !== b.param || a.equals !== b.equals) {
119
+ return false;
120
+ }
121
+
122
+ return sameArrayObjects(a.fields, b.fields, true);
123
+ }
124
+
125
+ paginationFiltersEqual(a: PaginationParam[], b: PaginationParam[]): boolean {
126
+ if (!!a && a?.length !== b?.length) {
127
+ return false;
128
+ }
129
+
130
+ for (let i = 0; i < a.length; i++) {
131
+ if (!this.paginationFilterEqual(a[i], b[i])) {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ return true;
137
+ }
138
+
139
+ paginationEqual(a?: PaginationArgs, b?: PaginationArgs): boolean {
140
+ const {
141
+ filters: aFilter = [], sort: aSort = [], projectsOrNamespaces: aPN = [], ...aPrimitiveTypes
142
+ } = a || {};
143
+ const {
144
+ filters: bFilter = [], sort: bSort = [], projectsOrNamespaces: bPN = [], ...bPrimitiveTypes
145
+ } = b || {};
146
+
147
+ return isEqual(aPrimitiveTypes, bPrimitiveTypes) &&
148
+ this.paginationFiltersEqual(aFilter, bFilter) &&
149
+ this.paginationFiltersEqual(aPN, bPN) &&
150
+ sameArrayObjects<PaginationSort>(aSort, bSort, true);
151
+ }
152
+ }
153
+
154
+ export default new PaginationUtils();
@@ -1,7 +1,7 @@
1
1
  import { reduce, filter, keys } from 'lodash';
2
2
  import { PSALabelPrefix, PSALabelsNamespaces } from '@shell/config/pod-security-admission';
3
3
  import { camelToTitle } from '@shell/utils/string';
4
- import { PSA } from '@shell/types/pod-security-admission';
4
+ import { PSA } from '@shell/types/resources/pod-security-admission';
5
5
 
6
6
  /**
7
7
  * Return PSA labels present in the resource
package/utils/router.js CHANGED
@@ -25,3 +25,89 @@ export function queryParamsFor(current, qp, defaults = {}) {
25
25
 
26
26
  return query;
27
27
  }
28
+
29
+ export function getClusterFromRoute(to) {
30
+ let cluster = to.params?.cluster;
31
+
32
+ if (!cluster) {
33
+ cluster = findMeta(to, 'cluster');
34
+ }
35
+
36
+ return cluster;
37
+ }
38
+
39
+ export function getProductFromRoute(to) {
40
+ let product = to.params?.product;
41
+
42
+ if ( !product ) {
43
+ const match = to.name?.match(/^c-cluster-([^-]+)/);
44
+
45
+ if ( match ) {
46
+ product = match[1];
47
+ }
48
+ }
49
+
50
+ // If still no product, see if the route indicates the product via route metadata
51
+ if (!product) {
52
+ product = findMeta(to, 'product');
53
+ }
54
+
55
+ return product;
56
+ }
57
+
58
+ export const getPackageFromRoute = (route) => {
59
+ if (!route?.meta) {
60
+ return;
61
+ }
62
+ // Sometimes meta is an array... sometimes not
63
+ const arraySafe = Array.isArray(route.meta) ? route.meta : [route.meta];
64
+
65
+ return arraySafe.find((m) => !!m.pkg)?.pkg;
66
+ };
67
+
68
+ export const getResourceFromRoute = (to) => {
69
+ let resource = to.params?.resource;
70
+
71
+ if (!resource) {
72
+ resource = findMeta(to, 'resource');
73
+ }
74
+
75
+ return resource;
76
+ };
77
+
78
+ /**
79
+ * Given a route it will look through the matching parent routes to see if any match the fn (predicate) criteria
80
+ *
81
+ * @param {*} to a VueRouter Route object
82
+ * @param {*} fn fn is a predicate which is passed a matched route. It will return true to indicate there was a matching route and false otherwise
83
+ * @returns true if a matching route was found, false otherwise
84
+ */
85
+ export const routeMatched = (to, fn) => {
86
+ const matched = to?.matched || [];
87
+
88
+ return !!matched.find(fn);
89
+ };
90
+
91
+ /**
92
+ * Checks to see if the route requires authentication by taking a look at the route and it's parents 'meta' to see if it
93
+ * contains { requiresAuthentication: true }
94
+ * @param {*} to a VueRouter Route object
95
+ * @returns true if the route requires authentication, false otherwise
96
+ */
97
+ export const routeRequiresAuthentication = (to) => {
98
+ return routeMatched(to, (matched) => matched.meta?.requiresAuthentication);
99
+ };
100
+
101
+ function findMeta(route, key) {
102
+ if (route?.meta) {
103
+ const meta = Array.isArray(route.meta) ? route.meta : [route.meta];
104
+
105
+ for (let i = 0; i < meta.length; i++) {
106
+ if (meta[i][key]) {
107
+ return meta[i][key];
108
+ }
109
+ }
110
+ }
111
+
112
+ return undefined;
113
+ }
package/utils/settings.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { MANAGEMENT } from '@shell/config/types';
2
2
  import { Store } from 'vuex';
3
3
  import { DEFAULT_PERF_SETTING, PerfSettings, SETTING } from '@shell/config/settings';
4
+ import { pluralize } from '@shell/utils/string';
5
+ import { _MULTI } from '@shell/plugins/dashboard-store/actions';
4
6
 
5
7
  export const fetchOrCreateSetting = async(store: Store<any>, id: string, val: string, save = true): Promise<any> => {
6
8
  let setting;
@@ -34,6 +36,50 @@ export const fetchSetting = async(store: Store<any>, id: string): Promise<any> =
34
36
  return setting;
35
37
  };
36
38
 
39
+ /**
40
+ * Carefully fetch mgmt settings
41
+ *
42
+ * Ensures that
43
+ * - Concurrent calls to this function will only result in a single http request
44
+ * - Subsequent calls, when either logged in or logged out, will only result in a single http request
45
+ * - Logged out call will fetch partial settings, after logging in another call will fetch all settings
46
+ *
47
+ * Will be used in many places, particularly multiple times when loading the dashboard
48
+ *
49
+ * Note - We need to specify the url for cases where it can't be determined (i.e. we haven't fetched schemas)
50
+ */
51
+ export const fetchInitialSettings = async(store: Store<any>): Promise<any> => {
52
+ const generation = store.getters['management/generation'](MANAGEMENT.SETTING);
53
+ // We use this as it copies the previous mechanism this was based on (in findAll)
54
+ // There is the getter `auth/loggedInAs` (which is set given `fromHeader`), but that's initialised after the first call to here (see `authenticated`)
55
+ const header = store.getters['auth/fromHeader'];
56
+ const authed = `${ header }` === 'true' || `${ header }` === 'none';
57
+
58
+ if (authed) {
59
+ // We're authed, we will always get the full list
60
+ return await store.dispatch('management/findAll', {
61
+ type: MANAGEMENT.SETTING,
62
+ opt: { url: `/v1/${ pluralize(MANAGEMENT.SETTING) }` }
63
+ } );
64
+ }
65
+
66
+ if (!generation) {
67
+ // We're not authed, and haven't previously fetched settings (no generation)
68
+ // Fetch settings, put them in the store, but don't say we've got all yet (so subsequent calls will run)
69
+ return await store.dispatch('management/findAll', {
70
+ type: MANAGEMENT.SETTING,
71
+ opt: {
72
+ url: `/v1/${ pluralize(MANAGEMENT.SETTING) }`,
73
+ load: _MULTI,
74
+ redirectUnauthorized: false
75
+ }
76
+ });
77
+ }
78
+
79
+ // We're not authed, but have a previous value, no need to make a http request to fetch again
80
+ return store.getters['management/all'](MANAGEMENT.SETTING);
81
+ };
82
+
37
83
  export const setSetting = async(store: Store<any>, id: string, val: string): Promise<any> => {
38
84
  const setting = await fetchOrCreateSetting(store, id, val, false);
39
85
 
package/utils/time.js CHANGED
@@ -10,7 +10,7 @@ export function diffFrom(value, from, t) {
10
10
  const now = day();
11
11
 
12
12
  from = from || now;
13
- const diff = value.diff(now, 'seconds');
13
+ const diff = value.diff(from, 'seconds');
14
14
 
15
15
  let absDiff = Math.abs(diff);
16
16
 
@@ -34,6 +34,7 @@ export function diffFrom(value, from, t) {
34
34
  diff,
35
35
  absDiff,
36
36
  label,
37
+ // i18n-uses unit.day, unit.hour, unit.min, unit.sec
37
38
  unitsKey: `unit.${ LABELS[i] }`,
38
39
  units: LABELS[i],
39
40
  next,
@@ -0,0 +1,36 @@
1
+ import { RouterLinkStub } from '@vue/test-utils';
2
+ import { NavigationFailure, Route } from 'vue-router';
3
+
4
+ /**
5
+ * See {@link RouterLinkSlotArgument} in vue-router
6
+ */
7
+ export interface RouterLinkSlotArgumentOptional {
8
+ href?: string;
9
+ route?: Route;
10
+ navigate?: (e?: MouseEvent) => Promise<undefined | NavigationFailure>;
11
+ isActive?: boolean;
12
+ isExactActive?: boolean;
13
+ }
14
+
15
+ /**
16
+ * This is a workaround because VueUtils RouterLinkStub doesn't currently support the slot api.
17
+ *
18
+ * See @link https://github.com/vuejs/vue-test-utils/issues/1803#issuecomment-940884170
19
+ *
20
+ * @param slotProps Provide arguments that you want passed to the child rendered by router-link
21
+ * @returns A stub
22
+ */
23
+ export function createChildRenderingRouterLinkStub(slotProps?: RouterLinkSlotArgumentOptional): typeof RouterLinkStub | any {
24
+ return {
25
+ ...RouterLinkStub,
26
+ render() {
27
+ return this.$scopedSlots.default({
28
+ href: slotProps?.href || '',
29
+ route: slotProps?.route || ({} as any),
30
+ navigate: slotProps?.navigate || (() => {}),
31
+ isActive: slotProps?.isActive || false,
32
+ isExactActive: slotProps?.isExactActive || false,
33
+ });
34
+ }
35
+ };
36
+ }
package/utils/url.ts CHANGED
@@ -2,7 +2,7 @@ type UriField = 'source' | 'protocol' | 'authority' | 'userInfo' | 'user' | 'pas
2
2
  type UriFields = {
3
3
  [key in UriField]: string; // eslint-disable-line no-unused-vars
4
4
  }
5
- type QueryParams = {
5
+ export type QueryParams = {
6
6
  [key: string]: string
7
7
  }
8
8
 
@@ -43,21 +43,87 @@ describe('formRules', () => {
43
43
  expect(formRuleResult).toStrictEqual(expectedResult);
44
44
  });
45
45
 
46
- it('"isHttps" : returns undefined when valid https url value is supplied', () => {
46
+ it('"https" : returns undefined when valid https url value is supplied', () => {
47
47
  const testValue = 'https://url.com';
48
- const formRuleResult = formRules.isHttps('server-url')(testValue);
48
+ const formRuleResult = formRules.https(testValue);
49
49
 
50
50
  expect(formRuleResult).toBeUndefined();
51
51
  });
52
52
 
53
- it('"isHttps" : returns correct message when http url value is supplied', () => {
53
+ it('"https" : returns correct message when http url value is supplied', () => {
54
54
  const testValue = 'http://url.com';
55
- const formRuleResult = formRules.isHttps('server-url')(testValue);
55
+ const formRuleResult = formRules.https(testValue);
56
56
  const expectedResult = JSON.stringify({ message: 'validation.setting.serverUrl.https' });
57
57
 
58
58
  expect(formRuleResult).toStrictEqual(expectedResult);
59
59
  });
60
60
 
61
+ describe('localhost', () => {
62
+ const message = JSON.stringify({ message: 'validation.setting.serverUrl.localhost' });
63
+ const testCases = [
64
+ ['http://LOCALhosT:8005', message],
65
+ ['http://localhost:8005', message],
66
+ ['https://localhost:8005', message],
67
+ ['localhost', message],
68
+ ['http://127.0.0.1', message],
69
+ ['https://127.0.0.1', message],
70
+ ['127.0.0.1', message],
71
+ ['https://test.com', undefined],
72
+ ['https://test.com/localhost', undefined],
73
+ [undefined, undefined]
74
+ ];
75
+
76
+ it.each(testCases)(
77
+ 'should return undefined or correct message based on the provided url',
78
+ (url, expected) => {
79
+ const formRuleResult = formRules.localhost(url);
80
+
81
+ expect(formRuleResult).toStrictEqual(expected);
82
+ }
83
+ );
84
+ });
85
+
86
+ describe('trailingForwardSlash', () => {
87
+ const message = JSON.stringify({ message: 'validation.setting.serverUrl.trailingForwardSlash' });
88
+ const testCases = [
89
+ ['https://test.com', undefined],
90
+ ['https://test.com/', message],
91
+ ['https://', undefined],
92
+ ['/', undefined],
93
+ [undefined, undefined]
94
+ ];
95
+
96
+ it.each(testCases)(
97
+ 'should return undefined or correct message based on the provided url',
98
+ (url, expected) => {
99
+ const formRuleResult = formRules.trailingForwardSlash(url);
100
+
101
+ expect(formRuleResult).toStrictEqual(expected);
102
+ }
103
+ );
104
+ });
105
+
106
+ describe('alphanumeric', () => {
107
+ const message = JSON.stringify({ message: 'validation.alphanumeric', key: 'testDisplayKey' });
108
+ const testCases = [
109
+ ['', undefined],
110
+ ['aaaAAAA111', undefined],
111
+ ['aaaAAAA111//', message],
112
+ ['/', message],
113
+ ['+1', message],
114
+ [undefined, undefined]
115
+ ];
116
+
117
+ it.each(testCases)(
118
+ 'should return undefined or correct message based on the provided url',
119
+ (val, expected) => {
120
+ const formRuleResult = formRules.alphanumeric(val);
121
+
122
+ expect(formRuleResult).toStrictEqual(expected);
123
+ }
124
+ );
125
+ });
126
+
61
127
  it('"interval" : returns undefined when valid hour interval value is supplied', () => {
62
128
  const testValue = '5h';
63
129
  const formRuleResult = formRules.interval(testValue);