@rancher/shell 0.3.0 → 0.3.2

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 (342) hide show
  1. package/assets/styles/global/_button.scss +5 -1
  2. package/assets/styles/global/_columns.scss +4 -0
  3. package/assets/styles/global/_gauges.scss +1 -1
  4. package/assets/styles/global/_layout.scss +5 -2
  5. package/assets/styles/global/_select.scss +1 -4
  6. package/assets/styles/themes/_dark.scss +5 -4
  7. package/assets/styles/themes/_light.scss +4 -3
  8. package/assets/styles/themes/_suse.scss +1 -1
  9. package/assets/styles/vendor/vue-select.scss +4 -3
  10. package/assets/translations/en-us.yaml +673 -73
  11. package/assets/translations/zh-hans.yaml +720 -207
  12. package/chart/monitoring/steps/uninstall-v1.vue +2 -2
  13. package/cloud-credential/azure.vue +23 -0
  14. package/cloud-credential/harvester.vue +25 -62
  15. package/cloud-credential/pnap.vue +80 -0
  16. package/components/.DS_Store +0 -0
  17. package/components/ActionMenu.vue +28 -7
  18. package/components/AdvancedSection.vue +9 -2
  19. package/components/Alert.vue +2 -2
  20. package/components/ButtonDropdown.vue +0 -2
  21. package/components/ButtonGroup.vue +1 -0
  22. package/components/CollapsibleCard.vue +0 -1
  23. package/components/CruResource.vue +41 -4
  24. package/components/DetailTop.vue +72 -4
  25. package/components/DisableAuthProviderModal.vue +106 -0
  26. package/{rancher-components/components/Utils/DraggableZone → components}/DraggableZone.vue +0 -0
  27. package/components/ExplorerMembers.vue +253 -30
  28. package/components/ExplorerProjectsNamespaces.vue +77 -33
  29. package/components/ExtensionPanel.vue +42 -0
  30. package/components/GrowlManager.vue +3 -3
  31. package/components/IconOrSvg.vue +178 -0
  32. package/components/LogItem.vue +69 -0
  33. package/components/PodSecurityAdmission.vue +302 -0
  34. package/components/PromptModal.vue +1 -0
  35. package/components/ResourceDetail/Masthead.vue +69 -4
  36. package/components/ResourceDetail/index.vue +12 -5
  37. package/components/ResourceList/Masthead.vue +11 -1
  38. package/components/ResourceList/ResourceLoadingIndicator.vue +12 -2
  39. package/components/ResourceList/index.vue +66 -12
  40. package/components/ResourceList/resource-list.config.js +7 -0
  41. package/components/ResourceTable.vue +33 -6
  42. package/components/SimpleBox.vue +1 -1
  43. package/components/SortableTable/THead.vue +21 -14
  44. package/components/SortableTable/filtering.js +1 -1
  45. package/components/SortableTable/index.vue +21 -10
  46. package/components/SortableTable/selection.js +15 -3
  47. package/components/Tabbed/Tab.vue +1 -1
  48. package/components/Tabbed/index.vue +20 -15
  49. package/components/__tests__/.DS_Store +0 -0
  50. package/components/__tests__/AsyncButton.test.ts +140 -0
  51. package/components/__tests__/BackLink.test.ts +33 -0
  52. package/components/__tests__/ButtonGroup.test.ts +124 -0
  53. package/components/__tests__/ClusterBadge.test.ts +32 -0
  54. package/components/__tests__/CollapsibleCard.test.ts +64 -0
  55. package/components/__tests__/ConsumptionGauge.test.ts +88 -0
  56. package/components/__tests__/CruResource.test.ts +3 -2
  57. package/components/__tests__/FixedBanner.test.ts +129 -0
  58. package/components/__tests__/GrowlManager.test.ts +147 -0
  59. package/components/__tests__/NamespaceFilter.test.ts +33 -25
  60. package/components/__tests__/PercentageBar.test.ts +32 -0
  61. package/components/__tests__/PodSecurityAdmission.test.ts +398 -0
  62. package/components/auth/AuthBanner.vue +20 -10
  63. package/components/auth/RoleDetailEdit.vue +26 -17
  64. package/components/auth/SelectPrincipal.vue +36 -5
  65. package/components/form/ArrayList.vue +3 -35
  66. package/components/form/ArrayListGrouped.vue +13 -4
  67. package/components/form/ArrayListSelect.vue +5 -5
  68. package/components/form/Error.vue +8 -0
  69. package/components/form/KeyValue.vue +39 -7
  70. package/components/form/LabeledSelect.vue +5 -2
  71. package/components/form/Labels.vue +46 -16
  72. package/components/form/Members/ClusterPermissionsEditor.vue +17 -17
  73. package/components/form/Members/MembershipEditor.vue +12 -12
  74. package/components/form/NameNsDescription.vue +1 -1
  75. package/components/form/NodeScheduling.vue +1 -1
  76. package/components/form/Probe.vue +3 -3
  77. package/components/form/ResourceQuota/Project.vue +6 -6
  78. package/components/form/ResourceTabs/index.vue +24 -6
  79. package/components/form/Security.vue +7 -6
  80. package/components/form/Select.vue +3 -2
  81. package/components/form/SelectOrCreateAuthSecret.vue +22 -29
  82. package/components/form/ServicePorts.vue +8 -0
  83. package/components/form/WorkloadPorts.vue +7 -1
  84. package/components/form/__tests__/ArrayList.test.ts +74 -0
  85. package/components/form/__tests__/ArrayListGrouped.test.ts +6 -4
  86. package/components/formatter/Checked.vue +1 -1
  87. package/components/formatter/ClusterLink.vue +5 -0
  88. package/components/formatter/IconIsDefault.vue +2 -2
  89. package/components/formatter/InternalExternalIP.vue +11 -8
  90. package/components/formatter/LiveDuration.vue +78 -0
  91. package/components/formatter/WorkloadHealthScale.vue +5 -3
  92. package/components/nav/Header.vue +74 -7
  93. package/components/nav/NamespaceFilter.vue +146 -63
  94. package/components/nav/TopLevelMenu.vue +22 -19
  95. package/components/nav/WindowManager/ContainerLogs.vue +83 -126
  96. package/components/nav/WindowManager/ContainerShell.vue +9 -7
  97. package/components/nav/WindowManager/Window.vue +2 -0
  98. package/components/nav/WindowManager/index.vue +10 -0
  99. package/config/elemental-types.js +9 -0
  100. package/config/features.js +2 -0
  101. package/config/home-links.js +4 -1
  102. package/config/pod-security-admission.ts +82 -0
  103. package/config/product/apps.js +1 -1
  104. package/config/product/auth.js +6 -5
  105. package/config/product/backup.js +1 -1
  106. package/config/product/explorer.js +6 -6
  107. package/config/product/fleet.js +1 -1
  108. package/config/product/manager.js +6 -2
  109. package/config/query-params.js +1 -0
  110. package/config/secret.js +0 -1
  111. package/config/settings.ts +26 -9
  112. package/config/table-headers.js +22 -11
  113. package/config/types.js +4 -1
  114. package/config/uiplugins.js +3 -3
  115. package/content/docs/zh-hans/getting-started.md +113 -137
  116. package/content/docs/zh-hans/whats-new.md +8 -46
  117. package/core/plugin-helpers.js +171 -0
  118. package/core/plugin.ts +61 -1
  119. package/core/plugins.js +33 -0
  120. package/core/types.ts +128 -2
  121. package/creators/pkg/package-lock.json +37 -0
  122. package/creators/pkg/package.json +1 -1
  123. package/detail/catalog.cattle.io.app.vue +1 -1
  124. package/detail/pod.vue +1 -1
  125. package/detail/provisioning.cattle.io.cluster.vue +35 -9
  126. package/detail/service.vue +2 -9
  127. package/detail/workload/index.vue +0 -1
  128. package/dialog/AddClusterMemberDialog.vue +22 -28
  129. package/dialog/AddProjectMemberDialog.vue +53 -9
  130. package/dialog/DiagnosticTimingsDialog.vue +8 -7
  131. package/dialog/DrainNode.vue +44 -48
  132. package/dialog/ForceMachineRemoveDialog.vue +5 -7
  133. package/dialog/GenericPrompt.vue +15 -20
  134. package/dialog/RollbackWorkloadDialog.vue +15 -46
  135. package/dialog/RotateCertificatesDialog.vue +5 -7
  136. package/dialog/RotateEncryptionKeyDialog.vue +5 -9
  137. package/dialog/SaveAsRKETemplateDialog.vue +5 -13
  138. package/dialog/ScaleMachineDownDialog.vue +1 -1
  139. package/dialog/ScalePoolDownDialog.vue +121 -0
  140. package/edit/__tests__/management.cattle.io.setting.test.ts +3 -3
  141. package/edit/auth/azuread.vue +16 -16
  142. package/edit/auth/github.vue +8 -0
  143. package/edit/auth/googleoauth.vue +10 -1
  144. package/edit/auth/ldap/index.vue +10 -0
  145. package/edit/auth/oidc.vue +10 -0
  146. package/edit/auth/saml.vue +10 -0
  147. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -1
  148. package/edit/catalog.cattle.io.clusterrepo.vue +3 -0
  149. package/edit/cloudcredential.vue +3 -7
  150. package/edit/logging-flow/Match.vue +39 -8
  151. package/edit/logging-flow/index.vue +27 -4
  152. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +107 -0
  153. package/edit/management.cattle.io.project.vue +8 -1
  154. package/edit/management.cattle.io.setting.vue +5 -2
  155. package/edit/management.cattle.io.user.vue +7 -1
  156. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +36 -8
  157. package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +2 -2
  158. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +14 -6
  159. package/edit/namespace.vue +18 -4
  160. package/edit/networking.k8s.io.ingress/Certificate.vue +1 -0
  161. package/edit/networking.k8s.io.ingress/IngressClass.vue +8 -6
  162. package/edit/networking.k8s.io.ingress/RulePath.vue +12 -6
  163. package/edit/networking.k8s.io.ingress/index.vue +8 -6
  164. package/edit/persistentvolume/index.vue +30 -27
  165. package/edit/persistentvolume/plugins/cephfs.vue +29 -29
  166. package/edit/persistentvolume/plugins/csi.vue +102 -62
  167. package/edit/persistentvolume/plugins/fc.vue +19 -19
  168. package/edit/persistentvolume/plugins/iscsi.vue +45 -45
  169. package/edit/persistentvolume/plugins/rbd.vue +39 -39
  170. package/edit/persistentvolumeclaim.vue +78 -75
  171. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -7
  172. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +10 -1
  173. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +87 -27
  174. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -6
  175. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +96 -0
  176. package/edit/provisioning.cattle.io.cluster/import.vue +1 -1
  177. package/edit/provisioning.cattle.io.cluster/index.vue +29 -6
  178. package/edit/provisioning.cattle.io.cluster/rke2.vue +445 -154
  179. package/edit/secret/index.vue +3 -7
  180. package/edit/service.vue +3 -1
  181. package/edit/storage.k8s.io.storageclass/index.vue +100 -16
  182. package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +114 -0
  183. package/edit/workload/__tests__/index.test.ts +98 -0
  184. package/edit/workload/index.vue +58 -8
  185. package/edit/workload/mixins/workload.js +107 -70
  186. package/edit/workload/storage/ContainerMountPaths.vue +0 -10
  187. package/edit/workload/storage/emptyDir.vue +88 -0
  188. package/edit/workload/storage/ephemeralVolume/index.vue +1 -1
  189. package/edit/workload/storage/index.vue +8 -0
  190. package/edit/workload/storage/persistentVolumeClaim/index.vue +1 -1
  191. package/layouts/default.vue +57 -44
  192. package/list/__tests__/workload.test.ts +5 -2
  193. package/list/catalog.cattle.io.app.vue +1 -0
  194. package/list/cis.cattle.io.clusterscan.vue +1 -0
  195. package/list/fleet.cattle.io.bundle.vue +5 -6
  196. package/list/fleet.cattle.io.cluster.vue +6 -3
  197. package/list/fleet.cattle.io.clusterregistrationtoken.vue +5 -6
  198. package/list/fleet.cattle.io.gitrepo.vue +4 -9
  199. package/list/helm.cattle.io.projecthelmchart.vue +1 -5
  200. package/list/logging.banzaicloud.io.clusterflow.vue +4 -1
  201. package/list/logging.banzaicloud.io.flow.vue +6 -5
  202. package/list/management.cattle.io.cluster.vue +1 -0
  203. package/list/management.cattle.io.feature.vue +3 -4
  204. package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +47 -0
  205. package/list/management.cattle.io.setting.vue +2 -2
  206. package/list/management.cattle.io.user.vue +4 -10
  207. package/list/monitoring.coreos.com.alertmanagerconfig.vue +2 -7
  208. package/list/node.vue +8 -5
  209. package/list/persistentvolume.vue +3 -3
  210. package/list/persistentvolumeclaim.vue +3 -4
  211. package/list/provisioning.cattle.io.cluster.vue +18 -19
  212. package/list/service.vue +6 -14
  213. package/list/workload.vue +43 -38
  214. package/machine-config/azure.vue +429 -60
  215. package/machine-config/pnap.vue +288 -0
  216. package/mixins/auth-config.js +1 -3
  217. package/mixins/browser-tab-visibility.js +8 -14
  218. package/mixins/chart.js +1 -1
  219. package/mixins/create-edit-view/impl.js +4 -0
  220. package/mixins/create-edit-view/index.js +4 -2
  221. package/mixins/resource-fetch-namespaced.js +98 -0
  222. package/mixins/resource-fetch.js +79 -45
  223. package/mixins/resource-manager.js +1 -23
  224. package/models/apps.controllerrevision.js +7 -0
  225. package/models/apps.daemonset.js +18 -0
  226. package/models/apps.deployment.js +44 -0
  227. package/models/apps.replicaset.js +7 -0
  228. package/models/apps.statefulset.js +18 -0
  229. package/models/batch.job.js +7 -14
  230. package/models/cluster/node.js +10 -2
  231. package/models/cluster.x-k8s.io.machine.js +26 -4
  232. package/models/cluster.x-k8s.io.machinedeployment.js +12 -2
  233. package/models/event.js +7 -0
  234. package/models/logging.banzaicloud.io.flow.js +4 -0
  235. package/models/management.cattle.io.cluster.js +1 -1
  236. package/models/management.cattle.io.clusterroletemplatebinding.js +1 -1
  237. package/models/management.cattle.io.globalrole.js +2 -2
  238. package/models/management.cattle.io.node.js +37 -2
  239. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +4 -0
  240. package/models/management.cattle.io.project.js +30 -11
  241. package/models/management.cattle.io.setting.js +1 -1
  242. package/models/management.cattle.io.user.js +37 -1
  243. package/models/namespace.js +42 -5
  244. package/models/persistentvolume.js +14 -2
  245. package/models/pod.js +15 -0
  246. package/models/projectroletemplatebinding.js +7 -0
  247. package/models/provisioning.cattle.io.cluster.js +61 -10
  248. package/models/rke-machine.cattle.io.pnapmachinetemplate.js +15 -0
  249. package/models/service.js +14 -13
  250. package/models/storage.k8s.io.storageclass.js +33 -18
  251. package/models/workload.js +38 -7
  252. package/nuxt.config.js +27 -17
  253. package/package.json +7 -7
  254. package/pages/about.vue +14 -2
  255. package/pages/c/_cluster/apps/charts/index.vue +21 -3
  256. package/pages/c/_cluster/apps/charts/install.vue +59 -22
  257. package/pages/c/_cluster/auth/config/_id.vue +6 -0
  258. package/pages/c/_cluster/auth/config/index.vue +8 -6
  259. package/pages/c/_cluster/auth/group.principal/assign-edit.vue +1 -1
  260. package/pages/c/_cluster/auth/roles/index.vue +1 -1
  261. package/pages/c/_cluster/explorer/index.vue +51 -6
  262. package/pages/c/_cluster/longhorn/index.vue +1 -1
  263. package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +15 -4
  264. package/pages/c/_cluster/monitoring/index.vue +1 -1
  265. package/pages/c/_cluster/neuvector/index.vue +1 -1
  266. package/pages/c/_cluster/settings/performance.vue +48 -2
  267. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +2 -0
  268. package/pages/c/_cluster/uiplugins/InstallDialog.vue +3 -0
  269. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +42 -2
  270. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +2 -0
  271. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +1 -0
  272. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +2 -0
  273. package/pages/c/_cluster/uiplugins/index.vue +42 -3
  274. package/pages/diagnostic.vue +5 -4
  275. package/pages/home.vue +105 -30
  276. package/pages/prefs.vue +23 -12
  277. package/pages/rio/mesh.vue +1 -1
  278. package/pkg/dynamic-importer.lib.js +8 -0
  279. package/pkg/vue.config.js +4 -0
  280. package/plugins/dashboard-store/__tests__/mutations.spec.js +406 -0
  281. package/plugins/dashboard-store/actions.js +32 -25
  282. package/plugins/dashboard-store/getters.js +50 -33
  283. package/plugins/dashboard-store/mutations.js +134 -28
  284. package/plugins/dashboard-store/resource-class.js +37 -42
  285. package/plugins/steve/actions.js +30 -0
  286. package/plugins/steve/caches/resourceCache.js +60 -0
  287. package/plugins/steve/getters.js +44 -1
  288. package/plugins/steve/mutations.js +97 -36
  289. package/plugins/steve/resourceWatcher.js +277 -0
  290. package/plugins/steve/schema.utils.js +25 -0
  291. package/plugins/steve/subscribe.js +288 -115
  292. package/plugins/steve/worker/index.js +17 -0
  293. package/plugins/steve/worker/web-worker.advanced.js +302 -0
  294. package/plugins/steve/{web-worker.steve-sub-worker.js → worker/web-worker.basic.js} +3 -44
  295. package/rancher-components/Card/Card.vue +3 -3
  296. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -0
  297. package/rancher-components/StringList/StringList.test.ts +45 -420
  298. package/rancher-components/StringList/StringList.vue +1 -10
  299. package/rancher-components/components/Banner/Banner.test.ts +44 -0
  300. package/rancher-components/components/Banner/Banner.vue +130 -61
  301. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +13 -22
  302. package/rancher-components/components/Form/Checkbox/Checkbox.vue +8 -6
  303. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +9 -9
  304. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -1
  305. package/rancher-components/components/StringList/StringList.test.ts +7 -7
  306. package/rancher-components/components/StringList/StringList.vue +21 -15
  307. package/scripts/test-plugins-build.sh +8 -0
  308. package/static/loading-indicator.html +1 -1
  309. package/store/action-menu.js +4 -3
  310. package/store/index.js +54 -3
  311. package/store/plugins.js +0 -17
  312. package/store/pnap.js +128 -0
  313. package/store/prefs.js +4 -2
  314. package/store/type-map.js +81 -13
  315. package/types/pod-security-admission.ts +36 -0
  316. package/types/shell/index.d.ts +497 -396
  317. package/utils/__tests__/object.test.ts +17 -1
  318. package/utils/__tests__/pod-security-admission.test.ts +61 -0
  319. package/utils/async.ts +36 -0
  320. package/utils/color.js +45 -0
  321. package/utils/crypto/browserHashUtils.js +18 -0
  322. package/utils/dynamic-importer.js +8 -0
  323. package/utils/install-redirect.js +1 -1
  324. package/utils/object.js +24 -0
  325. package/utils/pod-security-admission.ts +39 -0
  326. package/utils/socket.js +61 -24
  327. package/utils/string.js +2 -0
  328. package/utils/svg-filter.js +301 -0
  329. package/utils/time.js +49 -0
  330. package/utils/validators/cidr.js +4 -0
  331. package/utils/validators/formRules/__tests__/index.test.ts +23 -3
  332. package/utils/validators/formRules/index.ts +14 -0
  333. package/config/product/harvester-manager.js +0 -162
  334. package/edit/harvesterhci.io.management.cluster.vue +0 -153
  335. package/list/harvesterhci.io.management.cluster.vue +0 -241
  336. package/machine-config/harvester.vue +0 -693
  337. package/models/harvesterhci.io.management.cluster.js +0 -228
  338. package/pages/c/_cluster/harvesterManager/index.vue +0 -24
  339. package/rancher-components/Card/Card.test.ts +0 -39
  340. package/rancher-components/Utils/DraggableZone/DraggableZone.vue +0 -181
  341. package/rancher-components/Utils/DraggableZone/index.ts +0 -1
  342. package/rancher-components/components/Utils/DraggableZone/index.ts +0 -1
@@ -1,7 +1,8 @@
1
1
  import Vue from 'vue';
2
2
  import { addObject, addObjects, clear, removeObject } from '@shell/utils/array';
3
- import { SCHEMA } from '@shell/config/types';
4
- import { normalizeType } from '@shell/plugins/dashboard-store/normalize';
3
+ import { SCHEMA, COUNT } from '@shell/config/types';
4
+ import { normalizeType, keyFieldFor } from '@shell/plugins/dashboard-store/normalize';
5
+ import { addSchemaIndexFields } from '@shell/plugins/steve/schema.utils';
5
6
  import { classify } from '@shell/plugins/dashboard-store/classify';
6
7
  import garbageCollect from '@shell/utils/gc/gc';
7
8
 
@@ -10,12 +11,13 @@ function registerType(state, type) {
10
11
 
11
12
  if ( !cache ) {
12
13
  cache = {
13
- list: [],
14
- haveAll: false,
15
- haveSelector: {},
16
- revision: 0, // The highest known resourceVersion from the server for this type
17
- generation: 0, // Updated every time something is loaded for this type
18
- loadCounter: 0, // Used to cancel incremental loads if the page changes during load
14
+ list: [],
15
+ haveAll: false,
16
+ haveSelector: {},
17
+ haveNamespace: undefined, // If the cached list only contains resources for a namespace, this will contain the ns name
18
+ revision: 0, // The highest known resourceVersion from the server for this type
19
+ generation: 0, // Updated every time something is loaded for this type
20
+ loadCounter: 0, // Used to cancel incremental loads if the page changes during load
19
21
  };
20
22
 
21
23
  // Not enumerable so they don't get sent back to the client for SSR
@@ -31,6 +33,24 @@ function registerType(state, type) {
31
33
  return cache;
32
34
  }
33
35
 
36
+ export function replace(existing, data) {
37
+ for ( const k of Object.keys(existing) ) {
38
+ delete existing[k];
39
+ }
40
+
41
+ for ( const k of Object.keys(data) ) {
42
+ Vue.set(existing, k, data[k]);
43
+ }
44
+
45
+ return existing;
46
+ }
47
+
48
+ function replaceResource(existing, data, getters) {
49
+ data = getters.cleanResource(existing, data);
50
+
51
+ return replace(existing, data);
52
+ }
53
+
34
54
  export function load(state, { data, ctx, existing }) {
35
55
  const { getters } = ctx;
36
56
  let type = normalizeType(data.type);
@@ -40,8 +60,7 @@ export function load(state, { data, ctx, existing }) {
40
60
 
41
61
  // Inject special fields for indexing schemas
42
62
  if ( type === SCHEMA ) {
43
- data._id = normalizeType(data.id);
44
- data._group = normalizeType(data.attributes?.group);
63
+ addSchemaIndexFields(data);
45
64
  }
46
65
 
47
66
  const id = data[keyField];
@@ -52,24 +71,10 @@ export function load(state, { data, ctx, existing }) {
52
71
 
53
72
  let entry;
54
73
 
55
- function replace(existing, data) {
56
- data = getters.cleanResource(existing, data);
57
-
58
- for ( const k of Object.keys(existing) ) {
59
- delete existing[k];
60
- }
61
-
62
- for ( const k of Object.keys(data) ) {
63
- Vue.set(existing, k, data[k]);
64
- }
65
-
66
- return existing;
67
- }
68
-
69
74
  if ( existing && !existing.id ) {
70
75
  // A specific proxy instance to used was passed in (for create -> save),
71
76
  // use it instead of making a new proxy
72
- entry = replace(existing, data);
77
+ entry = replaceResource(existing, data, getters);
73
78
  addObject(cache.list, entry);
74
79
  cache.map.set(id, entry);
75
80
  // console.log('### Mutation added from existing proxy', type, id);
@@ -78,7 +83,7 @@ export function load(state, { data, ctx, existing }) {
78
83
 
79
84
  if ( entry ) {
80
85
  // There's already an entry in the store, update it
81
- replace(entry, data);
86
+ replaceResource(entry, data, getters);
82
87
  // console.log('### Mutation Updated', type, id);
83
88
  } else {
84
89
  // There's no entry, make a new proxy
@@ -115,6 +120,7 @@ export function forgetType(state, type) {
115
120
  if ( cache ) {
116
121
  cache.haveAll = false;
117
122
  cache.haveSelector = {};
123
+ cache.haveNamespace = undefined;
118
124
  cache.revision = 0;
119
125
  cache.generation = 0;
120
126
  clear(cache.list);
@@ -163,11 +169,99 @@ export function remove(state, obj, getters) {
163
169
  }
164
170
  }
165
171
 
172
+ export function batchChanges(state, { ctx, batch }) {
173
+ const batchTypes = Object.keys(batch);
174
+ const combinedBatch = {};
175
+
176
+ batchTypes.forEach((batchType) => {
177
+ combinedBatch[batchType] = batch[batchType];
178
+ const typeOption = ctx.rootGetters['type-map/optionsFor'](batchType);
179
+
180
+ if (typeOption?.alias?.length > 0) {
181
+ const alias = typeOption?.alias || [];
182
+
183
+ alias.forEach((aliasType) => {
184
+ combinedBatch[aliasType] = {};
185
+ for (const [key, value] of Object.entries(batch[batchType])) {
186
+ combinedBatch[aliasType][key] = {
187
+ ...value,
188
+ type: aliasType
189
+ };
190
+ }
191
+ });
192
+ }
193
+ });
194
+
195
+ const combinedBatchTypes = Object.keys(combinedBatch);
196
+
197
+ combinedBatchTypes.forEach((type) => {
198
+ const normalizedType = normalizeType(type === 'counts' ? COUNT : type);
199
+ const keyField = keyFieldFor(normalizedType);
200
+ const typeCache = registerType(state, normalizedType);
201
+
202
+ // making a map for every resource's location in the list is gonna ensure we only have to loop through the big list once.
203
+ const typeCacheIndexMap = {};
204
+
205
+ typeCache.list.forEach((resource, index) => {
206
+ typeCacheIndexMap[resource[keyField]] = index;
207
+ });
208
+
209
+ const removeAtIndexes = [];
210
+
211
+ // looping through the batch, executing changes, deferring creates and removes since they change the array length
212
+ Object.keys(batch[normalizedType]).forEach((id) => {
213
+ const index = typeCacheIndexMap[id];
214
+ const resource = batch[normalizedType][id];
215
+
216
+ // an empty resource passed into batch changes is how we'll signal which ones to delete
217
+ if (Object.keys(resource).length === 0 && index !== undefined) {
218
+ typeCache.map.delete(id);
219
+ removeAtIndexes.push(index);
220
+ } else if (Object.keys(resource).length === 0) {
221
+ // No op. We're removing it... but we don't have it in the cache
222
+ } else {
223
+ if (normalizedType === SCHEMA) {
224
+ addSchemaIndexFields(resource);
225
+ }
226
+ const classyResource = classify(ctx, resource);
227
+
228
+ if (index === undefined) {
229
+ typeCache.list.push(classyResource);
230
+ typeCache.map.set(id, classyResource);
231
+
232
+ typeCacheIndexMap[classyResource[keyField]] = typeCache.list.length - 1;
233
+ } else {
234
+ replaceResource(typeCache.list[index], resource, ctx.getters);
235
+ }
236
+ }
237
+ });
238
+
239
+ // looping through the removeAtIndexes, making sure to offset by iteration so the array changing doesn't mess us up
240
+ removeAtIndexes.sort().forEach((cacheIndex, loopIndex) => {
241
+ typeCache.list.splice(cacheIndex - loopIndex, 1);
242
+ });
243
+
244
+ const opts = ctx.rootGetters[`type-map/optionsFor`](type);
245
+ const limit = opts?.limit;
246
+
247
+ // If there is a limit to the number of resources we can store for this type then
248
+ // remove the first one to keep the list size to that limit
249
+ if (limit && typeCache.list.length > limit) {
250
+ const rm = typeCache.list.shift();
251
+
252
+ typeCache.map.delete(rm.id);
253
+ }
254
+
255
+ typeCache.generation++;
256
+ });
257
+ }
258
+
166
259
  export function loadAll(state, {
167
260
  type,
168
261
  data,
169
262
  ctx,
170
- skipHaveAll
263
+ skipHaveAll,
264
+ namespace
171
265
  }) {
172
266
  const { getters } = ctx;
173
267
 
@@ -199,7 +293,8 @@ export function loadAll(state, {
199
293
 
200
294
  // Allow requester to skip setting that everything has loaded
201
295
  if (!skipHaveAll) {
202
- cache.haveAll = true;
296
+ cache.haveNamespace = namespace;
297
+ cache.haveAll = !namespace;
203
298
  }
204
299
 
205
300
  return proxies;
@@ -238,6 +333,11 @@ export default {
238
333
 
239
334
  loadAll,
240
335
 
336
+ /**
337
+ * Handles changes (add, update, remove) to multiple resources for multiple types
338
+ */
339
+ batchChanges,
340
+
241
341
  loadMerge(state, { type, data: allLatest, ctx }) {
242
342
  const { commit, getters } = ctx;
243
343
  // const allLatest = await dispatch('findAll', { type, opt: { force: true, load, _NONE } });
@@ -288,6 +388,12 @@ export default {
288
388
  cache.haveAll = true;
289
389
  },
290
390
 
391
+ setHaveNamespace(state, { type, namespace }) {
392
+ const cache = registerType(state, type);
393
+
394
+ cache.haveNamespace = namespace;
395
+ },
396
+
291
397
  loadedAll(state, { type }) {
292
398
  const cache = registerType(state, type);
293
399
 
@@ -25,6 +25,7 @@ import {
25
25
  validateLength,
26
26
  } from '@shell/utils/validators';
27
27
  import formRulesGenerator from '@shell/utils/validators/formRules/index';
28
+ import { waitFor } from '@shell/utils/async';
28
29
  import jsyaml from 'js-yaml';
29
30
  import compact from 'lodash/compact';
30
31
  import forIn from 'lodash/forIn';
@@ -36,6 +37,9 @@ import Vue from 'vue';
36
37
 
37
38
  import { normalizeType } from './normalize';
38
39
 
40
+ import { ExtensionPoint, ActionLocation } from '@shell/core/types';
41
+ import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
42
+
39
43
  const STRING_LIKE_TYPES = [
40
44
  'string',
41
45
  'date',
@@ -752,41 +756,7 @@ export default class Resource {
752
756
  // ------------------------------------------------------------------
753
757
 
754
758
  waitForTestFn(fn, msg, timeoutMs, intervalMs) {
755
- console.log('Starting wait for', msg); // eslint-disable-line no-console
756
-
757
- if ( !timeoutMs ) {
758
- timeoutMs = DEFAULT_WAIT_TMIMEOUT;
759
- }
760
-
761
- if ( !intervalMs ) {
762
- intervalMs = DEFAULT_WAIT_INTERVAL;
763
- }
764
-
765
- return new Promise((resolve, reject) => {
766
- // Do a first check immediately
767
- if ( fn.apply(this) ) {
768
- console.log('Wait for', msg, 'done immediately'); // eslint-disable-line no-console
769
- resolve(this);
770
- }
771
-
772
- const timeout = setTimeout(() => {
773
- console.log('Wait for', msg, 'timed out'); // eslint-disable-line no-console
774
- clearInterval(interval);
775
- clearTimeout(timeout);
776
- reject(new Error(`Failed waiting for: ${ msg }`));
777
- }, timeoutMs);
778
-
779
- const interval = setInterval(() => {
780
- if ( fn.apply(this) ) {
781
- console.log('Wait for', msg, 'done'); // eslint-disable-line no-console
782
- clearInterval(interval);
783
- clearTimeout(timeout);
784
- resolve(this);
785
- } else {
786
- console.log('Wait for', msg, 'not done yet'); // eslint-disable-line no-console
787
- }
788
- }, intervalMs);
789
- });
759
+ return waitFor(() => fn.apply(this), msg, timeoutMs || DEFAULT_WAIT_TMIMEOUT, intervalMs || DEFAULT_WAIT_INTERVAL, true);
790
760
  }
791
761
 
792
762
  waitForState(state, timeout, interval) {
@@ -798,19 +768,19 @@ export default class Resource {
798
768
  waitForTransition() {
799
769
  return this.waitForTestFn(() => {
800
770
  return !this.transitioning;
801
- }, 'transition completion');
771
+ }, 'transition completion', undefined, undefined);
802
772
  }
803
773
 
804
774
  waitForAction(name) {
805
775
  return this.waitForTestFn(() => {
806
776
  return this.hasAction(name);
807
- }, `action=${ name }`);
777
+ }, `action=${ name }`, undefined, undefined);
808
778
  }
809
779
 
810
780
  waitForLink(name) {
811
781
  return this.waitForTestFn(() => {
812
782
  return this.hasLink(name);
813
- }, `link=${ name }`);
783
+ }, `link=${ name }`, undefined, undefined);
814
784
  }
815
785
 
816
786
  hasCondition(condition) {
@@ -884,7 +854,11 @@ export default class Resource {
884
854
 
885
855
  // You can add custom actions by overriding your own availableActions (and probably reading super._availableActions)
886
856
  get _availableActions() {
887
- const all = [
857
+ // get menu actions available by plugins configuration
858
+ const currentRoute = this.currentRouter().app._route;
859
+ const extensionMenuActions = getApplicableExtensionEnhancements(this.$rootState, ExtensionPoint.ACTION, ActionLocation.TABLE, currentRoute, this);
860
+
861
+ let all = [
888
862
  { divider: true },
889
863
  {
890
864
  action: this.canUpdate ? 'goToEdit' : 'goToViewConfig',
@@ -932,6 +906,13 @@ export default class Resource {
932
906
  },
933
907
  ];
934
908
 
909
+ // Extension actions get added to the end, so add a divider if there are any
910
+ if (extensionMenuActions.length) {
911
+ // Add a divider first
912
+ all.push({ divider: true });
913
+ all = all.concat(extensionMenuActions);
914
+ }
915
+
935
916
  return all;
936
917
  }
937
918
 
@@ -1075,9 +1056,16 @@ export default class Resource {
1075
1056
  return this._save(...arguments);
1076
1057
  }
1077
1058
 
1059
+ /**
1060
+ * Allow to handle the response of the save request
1061
+ * @param {*} res Full request response
1062
+ */
1063
+ processSaveResponse(res) { }
1064
+
1078
1065
  async _save(opt = {}) {
1079
1066
  delete this.__rehydrate;
1080
1067
  delete this.__clone;
1068
+
1081
1069
  const forNew = !this.id;
1082
1070
 
1083
1071
  const errors = await this.validationErrors(this, opt.ignoreFields);
@@ -1151,6 +1139,9 @@ export default class Resource {
1151
1139
  try {
1152
1140
  const res = await this.$dispatch('request', { opt, type: this.type } );
1153
1141
 
1142
+ // Allow to process response independently from the related models
1143
+ this.processSaveResponse(res);
1144
+
1154
1145
  // Steve sometimes returns Table responses instead of the resource you just saved.. ignore
1155
1146
  if ( res && res.kind !== 'Table') {
1156
1147
  await this.$dispatch('load', { data: res, existing: (forNew ? this : undefined ) });
@@ -1323,8 +1314,9 @@ export default class Resource {
1323
1314
 
1324
1315
  async download() {
1325
1316
  const value = await this.followLink('view', { headers: { accept: 'application/yaml' } });
1317
+ const data = await this.$dispatch('cleanForDownload', value.data);
1326
1318
 
1327
- downloadFile(`${ this.nameDisplay }.yaml`, value.data, 'application/yaml');
1319
+ downloadFile(`${ this.nameDisplay }.yaml`, data, 'application/yaml');
1328
1320
  }
1329
1321
 
1330
1322
  async downloadBulk(items) {
@@ -1343,8 +1335,11 @@ export default class Resource {
1343
1335
  }
1344
1336
 
1345
1337
  await eachLimit(items, 10, (item, idx) => {
1346
- return item.followLink('view', { headers: { accept: 'application/yaml' } } ).then((data) => {
1347
- files[`resources/${ names[idx] }`] = data.data || data;
1338
+ return item.followLink('view', { headers: { accept: 'application/yaml' } } ).then(async(data) => {
1339
+ const yaml = data.data || data;
1340
+ const cleanedYaml = await this.$dispatch('cleanForDownload', yaml);
1341
+
1342
+ files[`resources/${ names[idx] }`] = cleanedYaml;
1348
1343
  });
1349
1344
  });
1350
1345
 
@@ -7,6 +7,7 @@ import { streamJson, streamingSupported } from '@shell/utils/stream';
7
7
  import isObject from 'lodash/isObject';
8
8
  import { classify } from '@shell/plugins/dashboard-store/classify';
9
9
  import { NAMESPACE } from '@shell/config/types';
10
+ import jsyaml from 'js-yaml';
10
11
 
11
12
  export default {
12
13
 
@@ -323,6 +324,35 @@ export default {
323
324
 
324
325
  return resource;
325
326
  },
327
+
328
+ // remove fields added by steve before showing/downloading yamls
329
+ cleanForDownload(ctx, yaml) {
330
+ if (!yaml) {
331
+ return;
332
+ }
333
+ const rootKeys = [
334
+ 'id',
335
+ 'links',
336
+ 'type',
337
+ 'actions'
338
+ ];
339
+ const metadataKeys = [
340
+ 'fields',
341
+ 'relationships',
342
+ 'state',
343
+ ];
344
+ const conditionKeys = [
345
+ 'error',
346
+ 'transitioning',
347
+ ];
348
+ const obj = jsyaml.load(yaml);
349
+
350
+ dropKeys(obj, rootKeys);
351
+ dropKeys(obj?.metadata, metadataKeys);
352
+ (obj?.status?.conditions || []).forEach(condition => dropKeys(condition, conditionKeys));
353
+
354
+ return jsyaml.dump(obj);
355
+ }
326
356
  };
327
357
 
328
358
  const diffRootKeys = [
@@ -0,0 +1,60 @@
1
+ import { SCHEMA, COUNT } from '@shell/config/types';
2
+ import { keyFieldFor, normalizeType } from '@shell/plugins/dashboard-store/normalize';
3
+ import { addSchemaIndexFields } from '@shell/plugins/steve/schema.utils';
4
+ import { hashObj } from '@shell/utils/crypto/browserHashUtils';
5
+
6
+ export default class ResourceCache {
7
+ resources = {};
8
+ type;
9
+ keyField;
10
+ constructor(type) {
11
+ this.type = normalizeType(type === 'counts' ? COUNT : type);
12
+ this.keyField = keyFieldFor(this.type);
13
+ }
14
+
15
+ load(collection = []) {
16
+ // console.time('startSchemaLoads!!!');
17
+ for (let i = 0; i < collection.length; i++) {
18
+ const resource = collection[i];
19
+
20
+ if ( this.type === SCHEMA ) {
21
+ addSchemaIndexFields(resource);
22
+ }
23
+ const id = resource[this.keyField];
24
+
25
+ // Store the hash instead of the whole object. This means longer load time be reduces memory footprint
26
+ // Perf Note: 3.328125 ms to load ~2500 schemas as objects into cache
27
+ // Perf Note: 67.450927734375 ms to load ~2500 schemas as hashes into cache
28
+ this.resources[id] = hashObj(resource);
29
+ }
30
+ // console.timeEnd('startSchemaLoads!!!');
31
+
32
+ return this;
33
+ }
34
+
35
+ change(resource, callback) {
36
+ if ( this.type === SCHEMA ) {
37
+ addSchemaIndexFields(resource);
38
+ }
39
+
40
+ const existingResourceHash = this.resources[resource[this.keyField]] || {};
41
+ const newResourceHash = hashObj(resource);
42
+
43
+ if (existingResourceHash !== newResourceHash) {
44
+ this.resources[resource[this.keyField]] = resource;
45
+ callback();
46
+ }
47
+ }
48
+
49
+ create(resource, callback) {
50
+ // ToDo: the logic for create is identical to change in these caches but the worker doesn't know that
51
+ this.change(resource, callback);
52
+ }
53
+
54
+ remove(id, callback) {
55
+ if (this.resources[id]) {
56
+ delete this.resources[id];
57
+ callback();
58
+ }
59
+ }
60
+ }
@@ -6,6 +6,8 @@ import { NAMESPACE, SCHEMA, COUNT, UI } from '@shell/config/types';
6
6
  import SteveModel from './steve-class';
7
7
  import HybridModel, { cleanHybridResources } from './hybrid-class';
8
8
  import NormanModel from './norman-class';
9
+ import { urlFor } from '@shell/plugins/dashboard-store/getters';
10
+ import { normalizeType } from '@shell/plugins/dashboard-store/normalize';
9
11
 
10
12
  export const STEVE_MODEL_TYPES = {
11
13
  NORMAN: 'norman',
@@ -67,6 +69,20 @@ export default {
67
69
  return url;
68
70
  },
69
71
 
72
+ urlFor: (state, getters) => (type, id, opt) => {
73
+ let url = urlFor(state, getters)(type, id, opt);
74
+
75
+ if (opt.namespaced) {
76
+ const parts = url.split('/');
77
+
78
+ url = `${ parts.join('/') }/${ opt.namespaced }`;
79
+
80
+ return url;
81
+ }
82
+
83
+ return url;
84
+ },
85
+
70
86
  defaultModel: state => (obj) => {
71
87
  const which = state.config.modelBaseClass || STEVE_MODEL_TYPES.BY_TYPE.STEVE;
72
88
 
@@ -106,6 +122,21 @@ export default {
106
122
  },
107
123
 
108
124
  cleanResource: () => (existing, data) => {
125
+ /**
126
+ * Resource counts are contained within a single 'count' resource with a 'counts' field that is a map of resource types
127
+ * When counts are updated through the websocket, only the resources that changed are sent so we can't load the new 'count' resource into the store as we would another resource
128
+ */
129
+ if (data?.type === COUNT && existing) {
130
+ data.counts = { ...existing.counts, ...data.counts };
131
+
132
+ return data;
133
+ }
134
+
135
+ // If the existing model has a cleanResource method, use it
136
+ if (existing?.cleanResource && typeof existing.cleanResource === 'function') {
137
+ return existing.cleanResource(data);
138
+ }
139
+
109
140
  const typeSuperClass = Object.getPrototypeOf(Object.getPrototypeOf(existing))?.constructor;
110
141
 
111
142
  return typeSuperClass === HybridModel ? cleanHybridResources(data) : data;
@@ -120,6 +151,18 @@ export default {
120
151
 
121
152
  gcIgnoreTypes: () => {
122
153
  return GC_IGNORE_TYPES;
123
- }
154
+ },
155
+
156
+ currentGeneration: state => (type) => {
157
+ type = normalizeType(type);
158
+
159
+ const cache = state.types[type];
160
+
161
+ if ( !cache ) {
162
+ return null;
163
+ }
164
+
165
+ return cache.generation;
166
+ },
124
167
 
125
168
  };