@rancher/shell 0.3.0 → 0.3.1

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 (322) 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/_layout.scss +1 -2
  4. package/assets/styles/global/_select.scss +1 -4
  5. package/assets/styles/themes/_dark.scss +4 -4
  6. package/assets/styles/themes/_light.scss +4 -3
  7. package/assets/styles/themes/_suse.scss +1 -1
  8. package/assets/styles/vendor/vue-select.scss +4 -3
  9. package/assets/translations/en-us.yaml +669 -73
  10. package/assets/translations/zh-hans.yaml +547 -165
  11. package/chart/monitoring/steps/uninstall-v1.vue +2 -2
  12. package/cloud-credential/azure.vue +23 -0
  13. package/cloud-credential/harvester.vue +25 -62
  14. package/cloud-credential/pnap.vue +80 -0
  15. package/components/.DS_Store +0 -0
  16. package/components/AdvancedSection.vue +9 -2
  17. package/components/Alert.vue +2 -2
  18. package/components/ButtonDropdown.vue +0 -2
  19. package/components/ButtonGroup.vue +1 -0
  20. package/components/CollapsibleCard.vue +0 -1
  21. package/components/CruResource.vue +41 -4
  22. package/components/DetailTop.vue +58 -3
  23. package/components/DisableAuthProviderModal.vue +106 -0
  24. package/{rancher-components/components/Utils/DraggableZone → components}/DraggableZone.vue +0 -0
  25. package/components/ExplorerMembers.vue +253 -30
  26. package/components/ExplorerProjectsNamespaces.vue +77 -33
  27. package/components/GrowlManager.vue +3 -3
  28. package/components/IconOrSvg.vue +149 -0
  29. package/components/LogItem.vue +69 -0
  30. package/components/PodSecurityAdmission.vue +302 -0
  31. package/components/PromptModal.vue +1 -0
  32. package/components/ResourceDetail/Masthead.vue +54 -2
  33. package/components/ResourceDetail/index.vue +12 -5
  34. package/components/ResourceList/Masthead.vue +11 -1
  35. package/components/ResourceList/ResourceLoadingIndicator.vue +12 -2
  36. package/components/ResourceList/index.vue +53 -12
  37. package/components/ResourceList/resource-list.config.js +7 -0
  38. package/components/ResourceTable.vue +31 -6
  39. package/components/SimpleBox.vue +1 -1
  40. package/components/SortableTable/THead.vue +15 -5
  41. package/components/SortableTable/index.vue +21 -10
  42. package/components/Tabbed/index.vue +20 -15
  43. package/components/__tests__/.DS_Store +0 -0
  44. package/components/__tests__/AsyncButton.test.ts +140 -0
  45. package/components/__tests__/BackLink.test.ts +33 -0
  46. package/components/__tests__/ButtonGroup.test.ts +124 -0
  47. package/components/__tests__/ClusterBadge.test.ts +32 -0
  48. package/components/__tests__/CollapsibleCard.test.ts +64 -0
  49. package/components/__tests__/ConsumptionGauge.test.ts +88 -0
  50. package/components/__tests__/CruResource.test.ts +3 -2
  51. package/components/__tests__/FixedBanner.test.ts +129 -0
  52. package/components/__tests__/GrowlManager.test.ts +147 -0
  53. package/components/__tests__/NamespaceFilter.test.ts +33 -25
  54. package/components/__tests__/PercentageBar.test.ts +32 -0
  55. package/components/__tests__/PodSecurityAdmission.test.ts +398 -0
  56. package/components/auth/AuthBanner.vue +20 -10
  57. package/components/auth/RoleDetailEdit.vue +26 -17
  58. package/components/auth/SelectPrincipal.vue +36 -5
  59. package/components/form/ArrayList.vue +3 -35
  60. package/components/form/ArrayListGrouped.vue +13 -4
  61. package/components/form/ArrayListSelect.vue +5 -5
  62. package/components/form/Error.vue +8 -0
  63. package/components/form/KeyValue.vue +39 -7
  64. package/components/form/LabeledSelect.vue +5 -2
  65. package/components/form/Labels.vue +46 -16
  66. package/components/form/Members/ClusterPermissionsEditor.vue +17 -17
  67. package/components/form/Members/MembershipEditor.vue +12 -12
  68. package/components/form/NameNsDescription.vue +1 -1
  69. package/components/form/NodeScheduling.vue +1 -1
  70. package/components/form/Probe.vue +3 -3
  71. package/components/form/ResourceQuota/Project.vue +6 -6
  72. package/components/form/ResourceTabs/index.vue +1 -6
  73. package/components/form/Security.vue +7 -6
  74. package/components/form/Select.vue +3 -2
  75. package/components/form/SelectOrCreateAuthSecret.vue +22 -29
  76. package/components/form/ServicePorts.vue +8 -0
  77. package/components/form/WorkloadPorts.vue +7 -1
  78. package/components/form/__tests__/ArrayList.test.ts +74 -0
  79. package/components/form/__tests__/ArrayListGrouped.test.ts +6 -4
  80. package/components/formatter/Checked.vue +1 -1
  81. package/components/formatter/ClusterLink.vue +5 -0
  82. package/components/formatter/IconIsDefault.vue +2 -2
  83. package/components/formatter/InternalExternalIP.vue +11 -8
  84. package/components/formatter/LiveDuration.vue +78 -0
  85. package/components/formatter/WorkloadHealthScale.vue +5 -3
  86. package/components/nav/Header.vue +6 -3
  87. package/components/nav/NamespaceFilter.vue +146 -63
  88. package/components/nav/TopLevelMenu.vue +22 -19
  89. package/components/nav/WindowManager/ContainerLogs.vue +83 -126
  90. package/components/nav/WindowManager/ContainerShell.vue +9 -7
  91. package/components/nav/WindowManager/Window.vue +2 -0
  92. package/components/nav/WindowManager/index.vue +10 -0
  93. package/config/elemental-types.js +9 -0
  94. package/config/features.js +2 -0
  95. package/config/home-links.js +4 -1
  96. package/config/pod-security-admission.ts +82 -0
  97. package/config/product/apps.js +1 -1
  98. package/config/product/auth.js +6 -5
  99. package/config/product/explorer.js +6 -6
  100. package/config/product/fleet.js +1 -1
  101. package/config/product/manager.js +6 -2
  102. package/config/secret.js +0 -1
  103. package/config/settings.ts +26 -9
  104. package/config/table-headers.js +22 -11
  105. package/config/types.js +4 -1
  106. package/content/docs/zh-hans/getting-started.md +113 -137
  107. package/content/docs/zh-hans/whats-new.md +8 -46
  108. package/creators/pkg/package-lock.json +37 -0
  109. package/creators/pkg/package.json +1 -1
  110. package/detail/catalog.cattle.io.app.vue +1 -1
  111. package/detail/pod.vue +1 -1
  112. package/detail/provisioning.cattle.io.cluster.vue +35 -9
  113. package/detail/service.vue +2 -9
  114. package/detail/workload/index.vue +0 -1
  115. package/dialog/AddClusterMemberDialog.vue +22 -28
  116. package/dialog/AddProjectMemberDialog.vue +53 -9
  117. package/dialog/DiagnosticTimingsDialog.vue +8 -7
  118. package/dialog/DrainNode.vue +44 -48
  119. package/dialog/ForceMachineRemoveDialog.vue +5 -7
  120. package/dialog/GenericPrompt.vue +15 -20
  121. package/dialog/RollbackWorkloadDialog.vue +15 -46
  122. package/dialog/RotateCertificatesDialog.vue +5 -7
  123. package/dialog/RotateEncryptionKeyDialog.vue +5 -9
  124. package/dialog/SaveAsRKETemplateDialog.vue +5 -13
  125. package/dialog/ScaleMachineDownDialog.vue +1 -1
  126. package/dialog/ScalePoolDownDialog.vue +121 -0
  127. package/edit/__tests__/management.cattle.io.setting.test.ts +3 -3
  128. package/edit/auth/azuread.vue +16 -16
  129. package/edit/auth/github.vue +8 -0
  130. package/edit/auth/googleoauth.vue +10 -1
  131. package/edit/auth/ldap/index.vue +10 -0
  132. package/edit/auth/oidc.vue +10 -0
  133. package/edit/auth/saml.vue +10 -0
  134. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -1
  135. package/edit/cloudcredential.vue +3 -7
  136. package/edit/logging-flow/Match.vue +39 -8
  137. package/edit/logging-flow/index.vue +27 -4
  138. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +107 -0
  139. package/edit/management.cattle.io.project.vue +8 -1
  140. package/edit/management.cattle.io.setting.vue +5 -2
  141. package/edit/management.cattle.io.user.vue +7 -1
  142. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +23 -7
  143. package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +2 -2
  144. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +14 -6
  145. package/edit/namespace.vue +18 -4
  146. package/edit/networking.k8s.io.ingress/Certificate.vue +1 -0
  147. package/edit/networking.k8s.io.ingress/IngressClass.vue +8 -6
  148. package/edit/networking.k8s.io.ingress/RulePath.vue +12 -6
  149. package/edit/networking.k8s.io.ingress/index.vue +8 -6
  150. package/edit/persistentvolume/index.vue +30 -27
  151. package/edit/persistentvolume/plugins/cephfs.vue +29 -29
  152. package/edit/persistentvolume/plugins/csi.vue +102 -62
  153. package/edit/persistentvolume/plugins/fc.vue +19 -19
  154. package/edit/persistentvolume/plugins/iscsi.vue +45 -45
  155. package/edit/persistentvolume/plugins/rbd.vue +39 -39
  156. package/edit/persistentvolumeclaim.vue +78 -75
  157. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -7
  158. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +10 -1
  159. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +87 -27
  160. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -6
  161. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +93 -0
  162. package/edit/provisioning.cattle.io.cluster/import.vue +1 -1
  163. package/edit/provisioning.cattle.io.cluster/index.vue +29 -6
  164. package/edit/provisioning.cattle.io.cluster/rke2.vue +440 -152
  165. package/edit/secret/index.vue +3 -7
  166. package/edit/service.vue +3 -1
  167. package/edit/storage.k8s.io.storageclass/index.vue +100 -16
  168. package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +114 -0
  169. package/edit/workload/__tests__/index.test.ts +98 -0
  170. package/edit/workload/index.vue +58 -8
  171. package/edit/workload/mixins/workload.js +107 -70
  172. package/edit/workload/storage/ContainerMountPaths.vue +0 -10
  173. package/edit/workload/storage/emptyDir.vue +88 -0
  174. package/edit/workload/storage/ephemeralVolume/index.vue +1 -1
  175. package/edit/workload/storage/index.vue +8 -0
  176. package/edit/workload/storage/persistentVolumeClaim/index.vue +1 -1
  177. package/layouts/default.vue +57 -44
  178. package/list/__tests__/workload.test.ts +5 -2
  179. package/list/catalog.cattle.io.app.vue +1 -0
  180. package/list/cis.cattle.io.clusterscan.vue +1 -0
  181. package/list/fleet.cattle.io.bundle.vue +5 -6
  182. package/list/fleet.cattle.io.cluster.vue +6 -3
  183. package/list/fleet.cattle.io.clusterregistrationtoken.vue +5 -6
  184. package/list/fleet.cattle.io.gitrepo.vue +4 -9
  185. package/list/helm.cattle.io.projecthelmchart.vue +1 -5
  186. package/list/logging.banzaicloud.io.clusterflow.vue +4 -1
  187. package/list/logging.banzaicloud.io.flow.vue +6 -5
  188. package/list/management.cattle.io.cluster.vue +1 -0
  189. package/list/management.cattle.io.feature.vue +3 -4
  190. package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +47 -0
  191. package/list/management.cattle.io.setting.vue +2 -2
  192. package/list/management.cattle.io.user.vue +4 -10
  193. package/list/monitoring.coreos.com.alertmanagerconfig.vue +2 -7
  194. package/list/node.vue +8 -5
  195. package/list/persistentvolume.vue +3 -3
  196. package/list/persistentvolumeclaim.vue +3 -4
  197. package/list/provisioning.cattle.io.cluster.vue +18 -19
  198. package/list/service.vue +6 -14
  199. package/list/workload.vue +43 -38
  200. package/machine-config/azure.vue +429 -60
  201. package/machine-config/pnap.vue +288 -0
  202. package/mixins/auth-config.js +1 -3
  203. package/mixins/browser-tab-visibility.js +8 -14
  204. package/mixins/chart.js +1 -1
  205. package/mixins/create-edit-view/impl.js +4 -0
  206. package/mixins/create-edit-view/index.js +4 -2
  207. package/mixins/resource-fetch-namespaced.js +98 -0
  208. package/mixins/resource-fetch.js +79 -45
  209. package/mixins/resource-manager.js +1 -23
  210. package/models/apps.controllerrevision.js +7 -0
  211. package/models/apps.daemonset.js +18 -0
  212. package/models/apps.deployment.js +44 -0
  213. package/models/apps.replicaset.js +7 -0
  214. package/models/apps.statefulset.js +18 -0
  215. package/models/batch.job.js +7 -14
  216. package/models/cluster/node.js +10 -2
  217. package/models/cluster.x-k8s.io.machine.js +26 -4
  218. package/models/cluster.x-k8s.io.machinedeployment.js +12 -2
  219. package/models/event.js +7 -0
  220. package/models/logging.banzaicloud.io.flow.js +4 -0
  221. package/models/management.cattle.io.cluster.js +1 -1
  222. package/models/management.cattle.io.clusterroletemplatebinding.js +1 -1
  223. package/models/management.cattle.io.globalrole.js +2 -2
  224. package/models/management.cattle.io.node.js +37 -2
  225. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +4 -0
  226. package/models/management.cattle.io.project.js +30 -11
  227. package/models/management.cattle.io.setting.js +1 -1
  228. package/models/management.cattle.io.user.js +37 -1
  229. package/models/namespace.js +42 -5
  230. package/models/persistentvolume.js +14 -2
  231. package/models/pod.js +15 -0
  232. package/models/projectroletemplatebinding.js +7 -0
  233. package/models/provisioning.cattle.io.cluster.js +61 -10
  234. package/models/rke-machine.cattle.io.pnapmachinetemplate.js +15 -0
  235. package/models/service.js +14 -13
  236. package/models/storage.k8s.io.storageclass.js +33 -18
  237. package/models/workload.js +38 -7
  238. package/nuxt.config.js +27 -17
  239. package/package.json +7 -7
  240. package/pages/about.vue +14 -2
  241. package/pages/c/_cluster/apps/charts/index.vue +4 -3
  242. package/pages/c/_cluster/apps/charts/install.vue +59 -22
  243. package/pages/c/_cluster/auth/config/_id.vue +6 -0
  244. package/pages/c/_cluster/auth/config/index.vue +8 -6
  245. package/pages/c/_cluster/auth/group.principal/assign-edit.vue +1 -1
  246. package/pages/c/_cluster/auth/roles/index.vue +1 -1
  247. package/pages/c/_cluster/explorer/index.vue +12 -6
  248. package/pages/c/_cluster/longhorn/index.vue +1 -1
  249. package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +15 -4
  250. package/pages/c/_cluster/monitoring/index.vue +1 -1
  251. package/pages/c/_cluster/neuvector/index.vue +1 -1
  252. package/pages/c/_cluster/settings/performance.vue +48 -2
  253. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +34 -1
  254. package/pages/c/_cluster/uiplugins/index.vue +28 -2
  255. package/pages/diagnostic.vue +5 -4
  256. package/pages/home.vue +105 -30
  257. package/pages/prefs.vue +23 -12
  258. package/pages/rio/mesh.vue +1 -1
  259. package/pkg/dynamic-importer.lib.js +8 -0
  260. package/pkg/vue.config.js +4 -0
  261. package/plugins/dashboard-store/__tests__/mutations.spec.js +406 -0
  262. package/plugins/dashboard-store/actions.js +32 -25
  263. package/plugins/dashboard-store/getters.js +50 -33
  264. package/plugins/dashboard-store/mutations.js +134 -28
  265. package/plugins/dashboard-store/resource-class.js +21 -41
  266. package/plugins/steve/actions.js +30 -0
  267. package/plugins/steve/caches/resourceCache.js +60 -0
  268. package/plugins/steve/getters.js +44 -1
  269. package/plugins/steve/mutations.js +97 -36
  270. package/plugins/steve/resourceWatcher.js +277 -0
  271. package/plugins/steve/schema.utils.js +25 -0
  272. package/plugins/steve/subscribe.js +288 -115
  273. package/plugins/steve/worker/index.js +17 -0
  274. package/plugins/steve/worker/web-worker.advanced.js +302 -0
  275. package/plugins/steve/{web-worker.steve-sub-worker.js → worker/web-worker.basic.js} +3 -44
  276. package/rancher-components/Card/Card.vue +3 -3
  277. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -0
  278. package/rancher-components/StringList/StringList.test.ts +45 -420
  279. package/rancher-components/StringList/StringList.vue +1 -10
  280. package/rancher-components/components/Banner/Banner.test.ts +44 -0
  281. package/rancher-components/components/Banner/Banner.vue +129 -61
  282. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +13 -22
  283. package/rancher-components/components/Form/Checkbox/Checkbox.vue +8 -6
  284. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +9 -9
  285. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -1
  286. package/rancher-components/components/StringList/StringList.test.ts +7 -7
  287. package/rancher-components/components/StringList/StringList.vue +21 -15
  288. package/scripts/test-plugins-build.sh +8 -0
  289. package/static/loading-indicator.html +1 -1
  290. package/store/index.js +54 -3
  291. package/store/plugins.js +0 -17
  292. package/store/pnap.js +128 -0
  293. package/store/prefs.js +4 -2
  294. package/store/type-map.js +55 -13
  295. package/types/pod-security-admission.ts +36 -0
  296. package/types/shell/index.d.ts +496 -396
  297. package/utils/__tests__/object.test.ts +17 -1
  298. package/utils/__tests__/pod-security-admission.test.ts +61 -0
  299. package/utils/async.ts +36 -0
  300. package/utils/color.js +45 -0
  301. package/utils/crypto/browserHashUtils.js +18 -0
  302. package/utils/dynamic-importer.js +8 -0
  303. package/utils/install-redirect.js +1 -1
  304. package/utils/object.js +24 -0
  305. package/utils/pod-security-admission.ts +39 -0
  306. package/utils/socket.js +61 -24
  307. package/utils/string.js +2 -0
  308. package/utils/svg-filter.js +301 -0
  309. package/utils/time.js +49 -0
  310. package/utils/validators/cidr.js +4 -0
  311. package/utils/validators/formRules/__tests__/index.test.ts +23 -3
  312. package/utils/validators/formRules/index.ts +14 -0
  313. package/config/product/harvester-manager.js +0 -162
  314. package/edit/harvesterhci.io.management.cluster.vue +0 -153
  315. package/list/harvesterhci.io.management.cluster.vue +0 -241
  316. package/machine-config/harvester.vue +0 -693
  317. package/models/harvesterhci.io.management.cluster.js +0 -228
  318. package/pages/c/_cluster/harvesterManager/index.vue +0 -24
  319. package/rancher-components/Card/Card.test.ts +0 -39
  320. package/rancher-components/Utils/DraggableZone/DraggableZone.vue +0 -181
  321. package/rancher-components/Utils/DraggableZone/index.ts +0 -1
  322. package/rancher-components/components/Utils/DraggableZone/index.ts +0 -1
@@ -1,6 +1,16 @@
1
+ /**
2
+ * Handles subscriptions to websockets which receive updates to resources
3
+ *
4
+ * Covers three use cases
5
+ * 1) Handles subscription within this file
6
+ * 2) Handles `cluster` subscriptions for some basic types in a web worker (SETTING.UI_PERFORMANCE advancedWorker = false)
7
+ * 2) Handles `cluster` subscriptions and optimisations in an advanced worker (SETTING.UI_PERFORMANCE advancedWorker = true)
8
+ */
9
+
1
10
  import { addObject, clear, removeObject } from '@shell/utils/array';
2
11
  import { get } from '@shell/utils/object';
3
- import { COUNT, SCHEMA } from '@shell/config/types';
12
+ import { SCHEMA } from '@shell/config/types';
13
+ import { CSRF } from '@shell/config/cookies';
4
14
  import { getPerformanceSetting } from '@shell/utils/settings';
5
15
  import Socket, {
6
16
  EVENT_CONNECTED,
@@ -8,25 +18,47 @@ import Socket, {
8
18
  EVENT_MESSAGE,
9
19
  // EVENT_FRAME_TIMEOUT,
10
20
  EVENT_CONNECT_ERROR,
11
- EVENT_DISCONNECT_ERROR
21
+ EVENT_DISCONNECT_ERROR,
22
+ NO_WATCH,
23
+ NO_SCHEMA,
12
24
  } from '@shell/utils/socket';
13
25
  import { normalizeType } from '@shell/plugins/dashboard-store/normalize';
14
26
  import day from 'dayjs';
15
27
  import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
16
28
  import { escapeHtml } from '@shell/utils/string';
29
+ import { keyForSubscribe } from '@shell/plugins/steve/resourceWatcher';
30
+ import { waitFor } from '@shell/utils/async';
17
31
 
18
32
  // eslint-disable-next-line
19
- import webworker from './web-worker.steve-sub-worker.js';
20
-
21
- export const NO_WATCH = 'NO_WATCH';
22
- export const NO_SCHEMA = 'NO_SCHEMA';
33
+ import storeWorker from './worker/index.js';
34
+ import { BLANK_CLUSTER } from '@shell/store/index.js';
23
35
 
24
36
  // minimum length of time a disconnect notification is shown
25
37
  const MINIMUM_TIME_NOTIFIED = 3000;
26
38
 
39
+ const waitForManagement = (store) => {
40
+ const managementReady = () => store.state?.managementReady;
41
+
42
+ return waitFor(managementReady, 'Management');
43
+ };
44
+
45
+ const isAdvancedWorker = (ctx) => {
46
+ const { rootGetters, getters } = ctx;
47
+ const storeName = getters.storeName;
48
+ const clusterId = rootGetters.clusterId;
49
+
50
+ if (storeName !== 'cluster' || clusterId === BLANK_CLUSTER) {
51
+ return false;
52
+ }
53
+
54
+ const perfSetting = getPerformanceSetting(rootGetters);
55
+
56
+ return perfSetting?.advancedWorker.enabled;
57
+ };
58
+
27
59
  // We only create a worker for the cluster store
28
- export function createWorker(store, ctx) {
29
- const { getters } = ctx;
60
+ export async function createWorker(store, ctx) {
61
+ const { getters, dispatch } = ctx;
30
62
  const storeName = getters.storeName;
31
63
 
32
64
  store.$workers = store.$workers || {};
@@ -35,24 +67,45 @@ export function createWorker(store, ctx) {
35
67
  return;
36
68
  }
37
69
 
70
+ await waitForManagement(store);
71
+ // getting perf setting in a separate constant here because it'll provide other values we'll want later.
72
+ const advancedWorker = isAdvancedWorker(ctx);
73
+
38
74
  const workerActions = {
39
75
  load: (resource) => {
40
76
  queueChange(ctx, resource, true, 'Change');
41
77
  },
42
78
  destroyWorker: () => {
43
79
  if (store.$workers) {
80
+ store.$workers[storeName].terminate();
44
81
  delete store.$workers[storeName];
45
82
  }
83
+ },
84
+ batchChanges: (batch) => {
85
+ dispatch('batchChanges', batch);
86
+ },
87
+ dispatch: (msg) => {
88
+ dispatch(`ws.${ msg.name }`, msg);
89
+ },
90
+ [EVENT_CONNECT_ERROR]: (e) => {
91
+ dispatch('error', e );
92
+ },
93
+ [EVENT_DISCONNECT_ERROR]: (e) => {
94
+ dispatch('error', e );
46
95
  }
47
96
  };
48
97
 
49
98
  if (!store.$workers[storeName]) {
50
- const worker = new webworker();
99
+ const workerMode = advancedWorker ? 'advanced' : 'basic';
100
+ const worker = storeWorker(workerMode);
51
101
 
52
102
  store.$workers[storeName] = worker;
53
103
 
54
104
  worker.postMessage({ initWorker: { storeName } });
55
105
 
106
+ /**
107
+ * Covers message from Worker to UI thread
108
+ */
56
109
  store.$workers[storeName].onmessage = (e) => {
57
110
  /* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
58
111
  // good enough for now considering that we never send more than one message action at a time right now */
@@ -65,14 +118,11 @@ export function createWorker(store, ctx) {
65
118
  }
66
119
  }
67
120
 
68
- export function keyForSubscribe({
69
- resourceType, type, namespace, id, selector
70
- } = {}) {
71
- return `${ resourceType || type || '' }/${ namespace || '' }/${ id || '' }/${ selector || '' }`;
72
- }
73
-
74
121
  export function equivalentWatch(a, b) {
75
- if ( a.type !== b.type ) {
122
+ const aresourceType = a.resourceType || a.type;
123
+ const bresourceType = b.resourceType || b.type;
124
+
125
+ if ( aresourceType !== bresourceType ) {
76
126
  return false;
77
127
  }
78
128
 
@@ -136,12 +186,17 @@ function growlsDisabled(rootGetters) {
136
186
  return getPerformanceSetting(rootGetters)?.disableWebsocketNotification;
137
187
  }
138
188
 
139
- export const actions = {
140
- subscribe(ctx, opt) {
189
+ /**
190
+ * Actions that cover all cases (see file description)
191
+ */
192
+ const sharedActions = {
193
+ async subscribe(ctx, opt) {
141
194
  const {
142
195
  state, commit, dispatch, getters, rootGetters
143
196
  } = ctx;
144
197
 
198
+ // ToDo: need to keep the worker up to date on CSRF cookie
199
+
145
200
  if (rootGetters['isSingleProduct']?.disableSteveSockets) {
146
201
  return;
147
202
  }
@@ -157,13 +212,28 @@ export const actions = {
157
212
  state.debugSocket && console.info(`Subscribe [${ getters.storeName }]`); // eslint-disable-line no-console
158
213
 
159
214
  const url = `${ state.config.baseUrl }/subscribe`;
215
+ const maxTries = growlsDisabled(rootGetters) ? null : 3;
216
+ const connectionMetadata = get(opt, 'metadata');
160
217
 
161
- if ( socket ) {
218
+ if (isAdvancedWorker(ctx)) {
219
+ if (!this.$workers[getters.storeName]) {
220
+ await createWorker(this, ctx);
221
+ }
222
+
223
+ // if the worker is in advanced mode then it'll contain it's own socket which it calls a 'watcher'
224
+ this.$workers[getters.storeName].postMessage({
225
+ createWatcher: {
226
+ connectionMetadata,
227
+ url: `${ state.config.baseUrl }/subscribe`,
228
+ csrf: this.$cookies.get(CSRF, { parseJSON: false }),
229
+ maxTries
230
+ }
231
+ });
232
+ } else if ( socket ) {
162
233
  socket.setAutoReconnect(true);
163
234
  socket.setUrl(url);
235
+ socket.connect(connectionMetadata);
164
236
  } else {
165
- const maxTries = growlsDisabled(rootGetters) ? null : 3;
166
-
167
237
  socket = new Socket(`${ state.config.baseUrl }/subscribe`, true, null, null, maxTries);
168
238
 
169
239
  commit('setSocket', socket);
@@ -194,75 +264,28 @@ export const actions = {
194
264
  }
195
265
  }
196
266
  });
267
+ socket.connect(connectionMetadata);
197
268
  }
198
-
199
- socket.connect(get(opt, 'metadata') );
200
269
  },
201
270
 
202
271
  unsubscribe({ commit, getters, state }) {
203
272
  const socket = state.socket;
204
- const worker = (this.$workers || {})[getters.storeName];
205
273
 
206
274
  commit('setWantSocket', false);
275
+ const cleanupTasks = [];
276
+
277
+ const worker = (this.$workers || {})[getters.storeName];
207
278
 
208
279
  if (worker) {
209
280
  worker.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
281
+ cleanupTasks.push(waitFor(() => !this.$workers[getters.storeName], 'Worker is destroyed'));
210
282
  }
211
283
 
212
284
  if ( socket ) {
213
- return socket.disconnect();
214
- }
215
- },
216
-
217
- async flush({
218
- state, commit, dispatch, getters
219
- }) {
220
- const queue = state.queue;
221
- let toLoad = [];
222
-
223
- if ( !queue.length ) {
224
- return;
225
- }
226
-
227
- const started = new Date().getTime();
228
-
229
- state.queue = [];
230
-
231
- state.debugSocket && console.debug(`Subscribe Flush [${ getters.storeName }]`, queue.length, 'items'); // eslint-disable-line no-console
232
-
233
- for ( const { action, event, body } of queue ) {
234
- if ( action === 'dispatch' && event === 'load' ) {
235
- // Group loads into one loadMulti when possible
236
- toLoad.push(body);
237
- } else {
238
- // When we hit a different kind of event, process all the previous loads, then the other event.
239
- if ( toLoad.length ) {
240
- await dispatch('loadMulti', toLoad);
241
- toLoad = [];
242
- }
243
-
244
- if ( action === 'dispatch' ) {
245
- await dispatch(event, body);
246
- } else if ( action === 'commit' ) {
247
- commit(event, body);
248
- } else {
249
- throw new Error('Invalid queued action');
250
- }
251
- }
285
+ cleanupTasks.push(socket.disconnect());
252
286
  }
253
287
 
254
- // Process any remaining loads
255
- if ( toLoad.length ) {
256
- await dispatch('loadMulti', toLoad);
257
- }
258
-
259
- state.debugSocket && console.debug(`Subscribe Flush [${ getters.storeName }] finished`, (new Date().getTime()) - started, 'ms'); // eslint-disable-line no-console
260
- },
261
-
262
- rehydrateSubscribe({ state, dispatch }) {
263
- if ( process.client && state.wantSocket && !state.socket ) {
264
- dispatch('subscribe');
265
- }
288
+ return Promise.all(cleanupTasks);
266
289
  },
267
290
 
268
291
  watch({
@@ -283,6 +306,7 @@ export const actions = {
283
306
  return;
284
307
  }
285
308
 
309
+ // If socket is in error don't try to watch.... unless we `force` it
286
310
  if ( !stop && !force && !getters.canWatch(params) ) {
287
311
  console.error(`Cannot Watch [${ getters.storeName }]`, JSON.stringify(params)); // eslint-disable-line no-console
288
312
 
@@ -323,9 +347,122 @@ export const actions = {
323
347
  msg.selector = selector;
324
348
  }
325
349
 
350
+ const worker = this.$workers[getters.storeName] || {};
351
+
352
+ if (worker.mode === 'advanced') {
353
+ if ( force ) {
354
+ msg.force = true;
355
+ }
356
+
357
+ worker.postMessage({ watch: msg });
358
+
359
+ return;
360
+ }
361
+
326
362
  return dispatch('send', msg);
327
363
  },
328
364
 
365
+ unwatch(ctx, type) {
366
+ const { commit, getters, dispatch } = ctx;
367
+
368
+ if (getters['schemaFor'](type)) {
369
+ const obj = {
370
+ type,
371
+ stop: true, // Stops the watch on a type
372
+ };
373
+
374
+ if (isAdvancedWorker(ctx)) {
375
+ dispatch('watch', obj); // Ask the backend to stop watching the type
376
+ } else if (getters['watchStarted'](obj)) {
377
+ // Set that we don't want to watch this type
378
+ // Otherwise, the dispatch to unwatch below will just cause a re-watch when we
379
+ // detect the stop message from the backend over the web socket
380
+ commit('setWatchStopped', obj);
381
+ dispatch('watch', obj); // Ask the backend to stop watching the type
382
+ // Make sure anything in the pending queue for the type is removed, since we've now removed the type
383
+ commit('clearFromQueue', type);
384
+ }
385
+ }
386
+ },
387
+
388
+ 'ws.ping'({ getters, dispatch }, msg) {
389
+ if ( getters.storeName === 'management' ) {
390
+ const version = msg?.data?.version || null;
391
+
392
+ dispatch('updateServerVersion', version, { root: true });
393
+ console.info(`Ping [${ getters.storeName }] from ${ version || 'unknown version' }`); // eslint-disable-line no-console
394
+ }
395
+ },
396
+ };
397
+
398
+ /**
399
+ * Mutations that cover all cases (both subscriptions here and in advanced worker)
400
+ */
401
+ const sharedMutations = {
402
+ debug(state, on, store) {
403
+ state.debugSocket = on !== false;
404
+ if (store && this.$workers[store]) {
405
+ this.$workers[store].postMessage({ toggleDebug: on !== false });
406
+ }
407
+ },
408
+ };
409
+
410
+ /**
411
+ * Actions that cover cases 1 & 2 (see file description)
412
+ */
413
+ const defaultActions = {
414
+
415
+ async flush({
416
+ state, commit, dispatch, getters
417
+ }) {
418
+ const queue = state.queue;
419
+ let toLoad = [];
420
+
421
+ if ( !queue.length ) {
422
+ return;
423
+ }
424
+
425
+ const started = new Date().getTime();
426
+
427
+ state.queue = [];
428
+
429
+ state.debugSocket && console.debug(`Subscribe Flush [${ getters.storeName }]`, queue.length, 'items'); // eslint-disable-line no-console
430
+
431
+ for ( const { action, event, body } of queue ) {
432
+ if ( action === 'dispatch' && event === 'load' ) {
433
+ // Group loads into one loadMulti when possible
434
+ toLoad.push(body);
435
+ } else {
436
+ // When we hit a different kind of event, process all the previous loads, then the other event.
437
+ if ( toLoad.length ) {
438
+ await dispatch('loadMulti', toLoad);
439
+ toLoad = [];
440
+ }
441
+
442
+ if ( action === 'dispatch' ) {
443
+ await dispatch(event, body);
444
+ } else if ( action === 'commit' ) {
445
+ commit(event, body);
446
+ } else {
447
+ throw new Error('Invalid queued action');
448
+ }
449
+ }
450
+ }
451
+
452
+ // Process any remaining loads
453
+ if ( toLoad.length ) {
454
+ await dispatch('loadMulti', toLoad);
455
+ }
456
+
457
+ state.debugSocket && console.debug(`Subscribe Flush [${ getters.storeName }] finished`, (new Date().getTime()) - started, 'ms'); // eslint-disable-line no-console
458
+ },
459
+
460
+ rehydrateSubscribe({ state, dispatch }) {
461
+ if ( process.client && state.wantSocket && !state.socket ) {
462
+ dispatch('subscribe');
463
+ }
464
+ },
465
+
329
466
  reconnectWatches({
330
467
  state, getters, commit, dispatch
331
468
  }) {
@@ -535,15 +672,9 @@ export const actions = {
535
672
  }
536
673
  },
537
674
 
538
- 'ws.ping'({ getters, dispatch }, msg) {
539
- if ( getters.storeName === 'management' ) {
540
- const version = msg?.data?.version || null;
541
-
542
- dispatch('updateServerVersion', version, { root: true });
543
- console.info(`Ping [${ getters.storeName }] from ${ version || 'unknown version' }`); // eslint-disable-line no-console
544
- }
545
- },
546
-
675
+ /**
676
+ * Steve only event
677
+ */
547
678
  'ws.resource.start'({ state, getters, commit }, msg) {
548
679
  state.debugSocket && console.info(`Resource start: [${ getters.storeName }]`, msg); // eslint-disable-line no-console
549
680
  commit('setWatchStarted', {
@@ -568,6 +699,14 @@ export const actions = {
568
699
  }
569
700
  },
570
701
 
702
+ /**
703
+ * Steve only event
704
+ *
705
+ * Steve only seems to send out `resource.stop` messages for two reasons
706
+ * - We have requested that the resource watch should be stopped and we receive this event as confirmation
707
+ * - Steve tells us that the resource is no longer watched
708
+ *
709
+ */
571
710
  'ws.resource.stop'({ getters, commit, dispatch }, msg) {
572
711
  const type = msg.resourceType;
573
712
  const obj = {
@@ -579,15 +718,44 @@ export const actions = {
579
718
 
580
719
  // console.warn(`Resource stop: [${ getters.storeName }]`, msg); // eslint-disable-line no-console
581
720
 
721
+ // If we're trying to watch this event, attempt to re-watch
582
722
  if ( getters['schemaFor'](type) && getters['watchStarted'](obj) ) {
583
- // Try reconnecting once
584
-
585
723
  commit('setWatchStopped', obj);
586
724
 
725
+ // In summary, we need to re-watch but with a reliable `revision` (to avoid `too old` message kicking off a full re-fetch of all
726
+ // resources). To get a reliable `revision` go out and fetch the latest for that resource type, in theory our local cache should be
727
+ // up to date with that revision.
728
+
729
+ const revisionExisting = getters.nextResourceVersion(type, obj.id);
730
+
731
+ let revisionLatest;
732
+
733
+ if (revisionExisting) {
734
+ // Attempt to fetch the latest revision at the time the resource watch was stopped, in theory our local cache should be up to
735
+ // date with this
736
+ // Ideally we shouldn't need to fetch here and supply `0`, `-1` or `null` to start watching from the latest revision, however steve
737
+ // will send the current state of each resource via a `resource.created` event.
738
+ const opt = { limit: 1 };
739
+
740
+ opt.url = getters.urlFor(type, null, opt);
741
+ revisionLatest = dispatch('request', { opt, type } )
742
+ .then(res => res.revision)
743
+ .catch((err) => {
744
+ // For some reason we can't fetch a reasonable revision, so force a re-fetch
745
+ console.warn(`Resource error retrieving resourceVersion, forcing re-fetch`, type, ':', err); // eslint-disable-line no-console
746
+ dispatch('resyncWatch', msg);
747
+ throw err;
748
+ });
749
+ } else {
750
+ // Some v1 resource types don't have revisions (either at the collection or resource level), so we avoided making an API request
751
+ // for them
752
+ revisionLatest = Promise.resolve(null); // Null to ensure we don't go through `nextResourceVersion` again
753
+ }
754
+
587
755
  setTimeout(() => {
588
756
  // Delay a bit so that immediate start/error/stop causes
589
757
  // only a slow infinite loop instead of a tight one.
590
- dispatch('watch', obj);
758
+ revisionLatest.then(revision => dispatch('watch', { ...obj, revision }));
591
759
  }, 5000);
592
760
  }
593
761
  },
@@ -600,16 +768,10 @@ export const actions = {
600
768
  const data = msg.data;
601
769
  const type = data.type;
602
770
 
603
- // Debounce count changes so we send at most 1 every 5 seconds
604
- if (type === COUNT) {
605
- const worker = (this.$workers || {})[ctx.getters.storeName];
606
-
607
- if (worker) {
608
- worker.postMessage({ countsUpdate: msg });
609
-
610
- // No further processing - let the web worker debounce the counts
611
- return;
612
- }
771
+ // Work-around for ws.error messages being sent as change events
772
+ // These have no id (or other metadata) which breaks lots if they are processed as change events
773
+ if (data.message && !data.id) {
774
+ return;
613
775
  }
614
776
 
615
777
  // Web worker can process schemas to check that they are actually changing and
@@ -677,7 +839,10 @@ export const actions = {
677
839
  },
678
840
  };
679
841
 
680
- export const mutations = {
842
+ /**
843
+ * Mutations that cover cases 1 & 2 (see file description)
844
+ */
845
+ const defaultMutations = {
681
846
  setSocket(state, socket) {
682
847
  state.socket = socket;
683
848
  },
@@ -726,21 +891,29 @@ export const mutations = {
726
891
  delete state.inError[key];
727
892
  },
728
893
 
729
- debug(state, on) {
730
- state.debugSocket = on !== false;
731
- },
732
-
733
894
  resetSubscriptions(state) {
895
+ // Clear out socket state. This is only ever called from reset... which is always called after we `disconnect` above.
896
+ // This could probably be folded in to there
734
897
  clear(state.started);
735
898
  clear(state.pendingFrames);
736
899
  clear(state.queue);
737
900
  clearTimeout(state.queueTimer);
738
901
  state.deferredRequests = {};
739
902
  state.queueTimer = null;
740
- }
903
+ },
904
+
905
+ clearFromQueue(state, type) {
906
+ // Remove anything in the queue that is a resource update for the given type
907
+ state.queue = state.queue.filter((item) => {
908
+ return item.body?.type !== type;
909
+ });
910
+ },
741
911
  };
742
912
 
743
- export const getters = {
913
+ /**
914
+ * Getters that cover cases 1 & 2 (see file description)
915
+ */
916
+ const defaultGetters = {
744
917
  canWatch: state => (obj) => {
745
918
  return !state.inError[keyForSubscribe(obj)];
746
919
  },
@@ -766,7 +939,7 @@ export const getters = {
766
939
  return null;
767
940
  }
768
941
 
769
- revision = cache.revision;
942
+ revision = cache.revision; // This is always zero.....
770
943
 
771
944
  for ( const obj of cache.list ) {
772
945
  if ( obj && obj.metadata ) {
@@ -783,16 +956,16 @@ export const getters = {
783
956
 
784
957
  return null;
785
958
  },
959
+ };
786
960
 
787
- currentGeneration: state => (type) => {
788
- type = normalizeType(type);
789
-
790
- const cache = state.types[type];
791
-
792
- if ( !cache ) {
793
- return null;
794
- }
961
+ export const actions = {
962
+ ...sharedActions,
963
+ ...defaultActions,
964
+ };
795
965
 
796
- return cache.generation;
797
- },
966
+ export const mutations = {
967
+ ...sharedMutations,
968
+ ...defaultMutations,
798
969
  };
970
+
971
+ export const getters = { ...defaultGetters };
@@ -0,0 +1,17 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import basicWorkerConstructor from '@shell/plugins/steve/worker/web-worker.basic.js';
3
+ // eslint-disable-next-line no-unused-vars
4
+ import advancedWorkerConstructor from '@shell/plugins/steve/worker/web-worker.advanced.js';
5
+
6
+ export default function storeWorker(mode, options = {}, closures = {}) {
7
+ let worker;
8
+
9
+ if (mode === 'advanced') {
10
+ worker = new advancedWorkerConstructor();
11
+ } else {
12
+ worker = new basicWorkerConstructor();
13
+ }
14
+ worker.mode = mode;
15
+
16
+ return worker;
17
+ }