@rancher/shell 3.0.6 → 3.0.7
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.
- package/assets/images/pl/dark/rancher-logo.svg +131 -44
- package/assets/images/pl/rancher-logo.svg +120 -44
- package/assets/styles/base/_basic.scss +2 -2
- package/assets/styles/base/_color-classic.scss +51 -0
- package/assets/styles/base/_color.scss +3 -3
- package/assets/styles/base/_mixins.scss +1 -1
- package/assets/styles/base/_variables-classic.scss +47 -0
- package/assets/styles/global/_button.scss +49 -17
- package/assets/styles/global/_form.scss +1 -1
- package/assets/styles/themes/_dark.scss +4 -0
- package/assets/styles/themes/_light.scss +3 -69
- package/assets/styles/themes/_modern.scss +194 -50
- package/assets/styles/vendor/vue-select.scss +1 -2
- package/assets/translations/en-us.yaml +33 -21
- package/components/ClusterIconMenu.vue +1 -1
- package/components/ClusterProviderIcon.vue +1 -1
- package/components/CodeMirror.vue +1 -1
- package/components/IconOrSvg.vue +40 -29
- package/components/ResourceDetail/index.vue +1 -0
- package/components/SortableTable/sorting.js +3 -1
- package/components/Tabbed/index.vue +5 -5
- package/components/form/ResourceTabs/index.vue +37 -18
- package/components/form/SecretSelector.vue +6 -2
- package/components/nav/Group.vue +29 -9
- package/components/nav/Header.vue +6 -8
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +47 -20
- package/components/nav/TopLevelMenu.vue +44 -14
- package/components/nav/Type.vue +0 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
- package/config/pagination-table-headers.js +10 -2
- package/config/product/explorer.js +4 -3
- package/config/table-headers.js +9 -0
- package/core/plugin.ts +18 -6
- package/core/types.ts +8 -0
- package/detail/provisioning.cattle.io.cluster.vue +1 -0
- package/dialog/InstallExtensionDialog.vue +71 -45
- package/dialog/UninstallExtensionDialog.vue +2 -1
- package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
- package/edit/auth/oidc.vue +86 -16
- package/mixins/__tests__/chart.test.ts +1 -1
- package/mixins/chart.js +1 -1
- package/models/event.js +7 -0
- package/models/provisioning.cattle.io.cluster.js +9 -0
- package/package.json +1 -1
- package/pages/c/_cluster/explorer/EventsTable.vue +3 -6
- package/pages/c/_cluster/settings/performance.vue +1 -1
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
- package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
- package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
- package/pages/c/_cluster/uiplugins/index.vue +110 -94
- package/plugins/__tests__/subscribe.events.test.ts +194 -0
- package/plugins/dashboard-store/actions.js +3 -0
- package/plugins/dashboard-store/getters.js +1 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
- package/plugins/steve/index.js +18 -10
- package/plugins/steve/mutations.js +2 -2
- package/plugins/steve/resourceWatcher.js +2 -2
- package/plugins/steve/steve-pagination-utils.ts +12 -9
- package/plugins/steve/subscribe.js +113 -85
- package/plugins/subscribe-events.ts +211 -0
- package/rancher-components/BadgeState/BadgeState.vue +8 -6
- package/rancher-components/Banner/Banner.vue +2 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
- package/rancher-components/Form/Radio/RadioButton.vue +3 -3
- package/store/index.js +12 -22
- package/types/extension-manager.ts +8 -1
- package/types/resources/settings.d.ts +24 -17
- package/types/shell/index.d.ts +352 -335
- package/types/store/subscribe-events.types.ts +70 -0
- package/types/store/subscribe.types.ts +6 -22
- package/utils/pagination-utils.ts +87 -28
- package/utils/pagination-wrapper.ts +6 -8
- package/utils/sort.js +5 -0
- package/utils/unit-tests/pagination-utils.spec.ts +283 -0
- package/utils/validators/formRules/__tests__/index.test.ts +7 -0
- package/utils/validators/formRules/index.ts +2 -2
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { actions, getters, mutations } from '../subscribe';
|
|
2
2
|
import { REVISION_TOO_OLD } from '../../../utils/socket';
|
|
3
|
-
import {
|
|
3
|
+
import { STEVE_WATCH_MODE } from '../../../types/store/subscribe.types';
|
|
4
4
|
import backOff from '../../../utils/back-off';
|
|
5
|
+
import { SteveWatchEventListenerManager } from '../../subscribe-events';
|
|
5
6
|
|
|
6
7
|
describe('steve: subscribe', () => {
|
|
7
8
|
describe('actions', () => {
|
|
8
9
|
describe('watch', () => {
|
|
9
|
-
const state = {};
|
|
10
|
+
const state = { listenerManager: new SteveWatchEventListenerManager() };
|
|
10
11
|
const getters = {
|
|
11
|
-
normalizeType:
|
|
12
|
-
schemaFor:
|
|
13
|
-
inError:
|
|
14
|
-
watchStarted:
|
|
12
|
+
normalizeType: (type: string) => type,
|
|
13
|
+
schemaFor: () => null,
|
|
14
|
+
inError: () => false,
|
|
15
|
+
watchStarted: () => false,
|
|
16
|
+
listenerManager: state.listenerManager
|
|
15
17
|
};
|
|
16
18
|
const rootGetters = {
|
|
17
19
|
'type-map/isSpoofed': () => false,
|
|
@@ -211,15 +213,11 @@ describe('steve: subscribe', () => {
|
|
|
211
213
|
}, {
|
|
212
214
|
...obj,
|
|
213
215
|
revision,
|
|
214
|
-
mode:
|
|
216
|
+
mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
|
|
215
217
|
force: true,
|
|
216
218
|
});
|
|
217
219
|
|
|
218
|
-
expect(dispatch).toHaveBeenNthCalledWith(1, '
|
|
219
|
-
id: undefined, mode: STEVE_WATCH_EVENT.CHANGES, namespace: undefined, selector: undefined, type: obj.type
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
expect(dispatch).toHaveBeenNthCalledWith(2, 'send', {
|
|
220
|
+
expect(dispatch).toHaveBeenNthCalledWith(1, 'send', {
|
|
223
221
|
debounceMs: 4000,
|
|
224
222
|
mode: 'resource.changes',
|
|
225
223
|
resourceType: obj.type,
|
|
@@ -231,7 +229,7 @@ describe('steve: subscribe', () => {
|
|
|
231
229
|
state, dispatch, getters, commit
|
|
232
230
|
}, { ...msg });
|
|
233
231
|
|
|
234
|
-
expect(dispatch).toHaveBeenCalledTimes(
|
|
232
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
235
233
|
dispatch.mockClear();
|
|
236
234
|
};
|
|
237
235
|
|
|
@@ -252,7 +250,7 @@ describe('steve: subscribe', () => {
|
|
|
252
250
|
});
|
|
253
251
|
expect(state.inError).toStrictEqual(
|
|
254
252
|
{
|
|
255
|
-
'type=abc,namespace=,id=,selector=': {
|
|
253
|
+
'type=abc,namespace=,id=,selector=,mode=resource.changes': {
|
|
256
254
|
obj: {
|
|
257
255
|
type: msg.resourceType,
|
|
258
256
|
mode: msg.mode,
|
|
@@ -268,7 +266,7 @@ describe('steve: subscribe', () => {
|
|
|
268
266
|
}, { ...msg });
|
|
269
267
|
// stop tries to watch again, however we're in error so will be ignored
|
|
270
268
|
expect(dispatch).toHaveBeenNthCalledWith(1, 'watch', {
|
|
271
|
-
id: undefined, mode:
|
|
269
|
+
id: undefined, mode: STEVE_WATCH_MODE.RESOURCE_CHANGES, namespace: undefined, selector: undefined, standardWatch: true, type: obj.type
|
|
272
270
|
});
|
|
273
271
|
|
|
274
272
|
dispatch.mockClear();
|
|
@@ -349,19 +347,24 @@ describe('steve: subscribe', () => {
|
|
|
349
347
|
const obj = { type: 'abc' };
|
|
350
348
|
const msg = {
|
|
351
349
|
resourceType: obj.type,
|
|
352
|
-
mode:
|
|
350
|
+
mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
|
|
353
351
|
};
|
|
354
352
|
|
|
355
353
|
const initStore = () => {
|
|
356
|
-
const state = {
|
|
354
|
+
const state = {
|
|
355
|
+
started: [],
|
|
356
|
+
inError: {},
|
|
357
|
+
listenerManager: new SteveWatchEventListenerManager()
|
|
358
|
+
};
|
|
357
359
|
const _getters = {
|
|
358
|
-
normalizeType:
|
|
359
|
-
schemaFor:
|
|
360
|
-
storeName:
|
|
361
|
-
inError:
|
|
362
|
-
watchStarted:
|
|
363
|
-
backOffId:
|
|
364
|
-
canBackoff:
|
|
360
|
+
normalizeType: (type: string) => type,
|
|
361
|
+
schemaFor: () => ({}),
|
|
362
|
+
storeName: 'test',
|
|
363
|
+
inError: (...args) => getters.inError(state)(...args),
|
|
364
|
+
watchStarted: (...args) => getters.watchStarted(state)(...args),
|
|
365
|
+
backOffId: (...args) => getters.backOffId()(...args),
|
|
366
|
+
canBackoff: () => true,
|
|
367
|
+
listenerManager: state.listenerManager
|
|
365
368
|
};
|
|
366
369
|
const commit = (type, ...args) => mutations[type](state, ...args);
|
|
367
370
|
|
package/plugins/steve/index.js
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
import getters, { STEVE_MODEL_TYPES } from './getters';
|
|
10
10
|
import mutations from './mutations';
|
|
11
11
|
import actions from './actions';
|
|
12
|
+
import { SteveWatchEventListenerManager } from '@shell/plugins/subscribe-events';
|
|
13
|
+
import { markRaw } from 'vue';
|
|
12
14
|
|
|
13
15
|
export function SteveFactory(namespace, baseUrl) {
|
|
14
16
|
return {
|
|
@@ -17,16 +19,22 @@ export function SteveFactory(namespace, baseUrl) {
|
|
|
17
19
|
state() {
|
|
18
20
|
return {
|
|
19
21
|
...coreStoreState(namespace, baseUrl),
|
|
20
|
-
socket:
|
|
21
|
-
queue:
|
|
22
|
-
wantSocket:
|
|
23
|
-
debugSocket:
|
|
24
|
-
allowStreaming:
|
|
25
|
-
pendingFrames:
|
|
26
|
-
deferredRequests:
|
|
27
|
-
started:
|
|
28
|
-
inError:
|
|
29
|
-
|
|
22
|
+
socket: null,
|
|
23
|
+
queue: [], // For change event coalescing
|
|
24
|
+
wantSocket: false,
|
|
25
|
+
debugSocket: false,
|
|
26
|
+
allowStreaming: true,
|
|
27
|
+
pendingFrames: [],
|
|
28
|
+
deferredRequests: {},
|
|
29
|
+
started: [],
|
|
30
|
+
inError: {},
|
|
31
|
+
/**
|
|
32
|
+
* Socket listener manager for this store
|
|
33
|
+
*
|
|
34
|
+
* Instance of @SteveWatchEventListenerManager . See it's description for more info
|
|
35
|
+
*/
|
|
36
|
+
socketListenerManager: markRaw(new SteveWatchEventListenerManager()),
|
|
37
|
+
podsByNamespace: {}, // Cache of pods by namespace
|
|
30
38
|
};
|
|
31
39
|
},
|
|
32
40
|
|
|
@@ -164,10 +164,10 @@ export default {
|
|
|
164
164
|
},
|
|
165
165
|
|
|
166
166
|
reset(state) {
|
|
167
|
-
// Reset generic store things
|
|
168
|
-
|
|
167
|
+
// 1. Reset generic store things
|
|
169
168
|
resetStore(state, this.commit);
|
|
170
169
|
|
|
170
|
+
// 2. Reset steve specific store things
|
|
171
171
|
this.commit(`${ state.config.namespace }/resetSubscriptions`);
|
|
172
172
|
|
|
173
173
|
// Clear the podsByNamespace cache
|
|
@@ -40,10 +40,10 @@ export const WATCH_STATUSES = {
|
|
|
40
40
|
* Create a unique key for a specific resource watch's params
|
|
41
41
|
*/
|
|
42
42
|
export const keyForSubscribe = ({
|
|
43
|
-
resourceType, type, namespace, id, selector
|
|
43
|
+
resourceType, type, namespace, id, selector, mode
|
|
44
44
|
} = {}) => {
|
|
45
45
|
const keyMap = {
|
|
46
|
-
type: resourceType || type, namespace, id, selector
|
|
46
|
+
type: resourceType || type, namespace, id, selector, mode
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
return Object.entries(keyMap)
|
|
@@ -17,9 +17,10 @@ import {
|
|
|
17
17
|
} from '@shell/config/types';
|
|
18
18
|
import { CAPI as CAPI_LAB_AND_ANO, CATTLE_PUBLIC_ENDPOINTS, STORAGE, UI_PROJECT_SECRET_COPY } from '@shell/config/labels-annotations';
|
|
19
19
|
import { Schema } from '@shell/plugins/steve/schema';
|
|
20
|
-
import {
|
|
20
|
+
import { PaginationSettingsStores } from '@shell/types/resources/settings';
|
|
21
21
|
import paginationUtils from '@shell/utils/pagination-utils';
|
|
22
22
|
import { KubeLabelSelector, KubeLabelSelectorExpression } from '@shell/types/kube/kube-api';
|
|
23
|
+
import { parseField } from '@shell/utils/sort';
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* This is a workaround for a ts build issue found in check-plugins-build.
|
|
@@ -401,9 +402,13 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
401
402
|
|
|
402
403
|
const joined = opt.pagination.sort
|
|
403
404
|
.map((s) => {
|
|
404
|
-
|
|
405
|
+
// Use the same mechanism as local sorting to flip logic for asc/des
|
|
406
|
+
const { field, reverse } = parseField(s.field);
|
|
407
|
+
const asc = reverse ? !s.asc : s.asc;
|
|
405
408
|
|
|
406
|
-
|
|
409
|
+
this.validateField(validateFields, schema, field);
|
|
410
|
+
|
|
411
|
+
return `${ asc ? '' : '-' }${ this.convertArrayPath(field) }`;
|
|
407
412
|
})
|
|
408
413
|
.join(',');
|
|
409
414
|
|
|
@@ -615,7 +620,6 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
615
620
|
res.push(`filter=!${ labelKey }`);
|
|
616
621
|
break;
|
|
617
622
|
case 'Gt':
|
|
618
|
-
// Currently broken - see https://github.com/rancher/rancher/issues/50057
|
|
619
623
|
// Only applicable to node affinity (atm) - https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#operators
|
|
620
624
|
|
|
621
625
|
if (typeof exp.values !== 'string') {
|
|
@@ -628,7 +632,6 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
628
632
|
res.push(`filter=${ labelKey } > (${ exp.values })`);
|
|
629
633
|
break;
|
|
630
634
|
case 'Lt':
|
|
631
|
-
// Currently broken - see https://github.com/rancher/rancher/issues/50057
|
|
632
635
|
// Only applicable to node affinity (atm) - https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#operators
|
|
633
636
|
if (typeof exp.values !== 'string') {
|
|
634
637
|
console.error(`Skipping labelSelector to API filter param conversion for ${ exp.key }(Lt) as no value was supplied`); // eslint-disable-line no-console
|
|
@@ -649,7 +652,7 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
649
652
|
}
|
|
650
653
|
}
|
|
651
654
|
|
|
652
|
-
export const PAGINATION_SETTINGS_STORE_DEFAULTS:
|
|
655
|
+
export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStores = {
|
|
653
656
|
cluster: {
|
|
654
657
|
resources: {
|
|
655
658
|
enableAll: false,
|
|
@@ -662,7 +665,7 @@ export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStore = {
|
|
|
662
665
|
CATALOG.APP, CATALOG.OPERATION,
|
|
663
666
|
HPA, INGRESS, SERVICE,
|
|
664
667
|
PV, CONFIG_MAP, STORAGE_CLASS, PVC, SECRET,
|
|
665
|
-
WORKLOAD_TYPES.REPLICA_SET, WORKLOAD_TYPES.REPLICATION_CONTROLLER
|
|
668
|
+
WORKLOAD_TYPES.REPLICA_SET, WORKLOAD_TYPES.REPLICATION_CONTROLLER,
|
|
666
669
|
],
|
|
667
670
|
generic: true,
|
|
668
671
|
}
|
|
@@ -673,8 +676,8 @@ export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStore = {
|
|
|
673
676
|
enableAll: false,
|
|
674
677
|
enableSome: {
|
|
675
678
|
enabled: [
|
|
676
|
-
// { resource: CAPI.RANCHER_CLUSTER, context: ['home', 'side-bar'] },
|
|
677
|
-
// { resource: MANAGEMENT.CLUSTER, context: ['side-bar'] },
|
|
679
|
+
// { resource: CAPI.RANCHER_CLUSTER, context: ['home', 'side-bar'] },
|
|
680
|
+
// { resource: MANAGEMENT.CLUSTER, context: ['side-bar'] },
|
|
678
681
|
{ resource: CATALOG.APP, context: ['branding'] },
|
|
679
682
|
SECRET
|
|
680
683
|
],
|
|
@@ -23,11 +23,13 @@
|
|
|
23
23
|
* Successfully flow - watch
|
|
24
24
|
* 1. UI --> Rancher: _watch_ request
|
|
25
25
|
* 2. Rancher --> UI: `resource.start`. UI sets watch as started
|
|
26
|
+
* ...
|
|
26
27
|
* 3. Rancher --> UI: `resource.change` (contains data). UI caches data
|
|
27
28
|
*
|
|
28
29
|
* Successful flow - watch - new mode
|
|
29
30
|
* 1. UI --> Rancher: _watch_ request
|
|
30
31
|
* 2. Rancher --> UI: `resource.start`. UI sets watch as started
|
|
32
|
+
* ...
|
|
31
33
|
* 3. Rancher --> UI: `resource.changes` (contains no data). UI makes a HTTP request to fetch data
|
|
32
34
|
*
|
|
33
35
|
* Successful flow - unwatch
|
|
@@ -84,9 +86,10 @@ import { WORKER_MODES } from './worker';
|
|
|
84
86
|
import acceptOrRejectSocketMessage from './accept-or-reject-socket-message';
|
|
85
87
|
import { BLANK_CLUSTER, STORE } from '@shell/store/store-types.js';
|
|
86
88
|
import { _MERGE } from '@shell/plugins/dashboard-store/actions';
|
|
87
|
-
import {
|
|
89
|
+
import { STEVE_WATCH_EVENT_TYPES, STEVE_WATCH_MODE } from '@shell/types/store/subscribe.types';
|
|
88
90
|
import paginationUtils from '@shell/utils/pagination-utils';
|
|
89
91
|
import backOff from '@shell/utils/back-off';
|
|
92
|
+
import { SteveWatchEventListenerManager } from '@shell/plugins/subscribe-events';
|
|
90
93
|
|
|
91
94
|
// minimum length of time a disconnect notification is shown
|
|
92
95
|
const MINIMUM_TIME_NOTIFIED = 3000;
|
|
@@ -312,24 +315,6 @@ function growlsDisabled(rootGetters) {
|
|
|
312
315
|
return getPerformanceSetting(rootGetters)?.disableWebsocketNotification;
|
|
313
316
|
}
|
|
314
317
|
|
|
315
|
-
/**
|
|
316
|
-
* Supported events are listed
|
|
317
|
-
*
|
|
318
|
-
* of type { [key: STEVE_WATCH_EVENT]: STEVE_WATCH_EVENT_LISTENER[]}
|
|
319
|
-
*/
|
|
320
|
-
const listeners = { [STEVE_WATCH_EVENT.CHANGES]: [] };
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Given a started or error entry, is it compatible with the given change in mode?
|
|
324
|
-
*/
|
|
325
|
-
const shouldUnwatchIncompatible = (messageMeta, mode) => {
|
|
326
|
-
if (messageMeta.mode === STEVE_WATCH_EVENT.CHANGES) {
|
|
327
|
-
return mode !== STEVE_WATCH_EVENT.CHANGES;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return mode === STEVE_WATCH_EVENT.CHANGES;
|
|
331
|
-
};
|
|
332
|
-
|
|
333
318
|
/**
|
|
334
319
|
* clear the provided error, but also ensure any backoff request associated with it is cleared as well
|
|
335
320
|
*/
|
|
@@ -452,7 +437,7 @@ const sharedActions = {
|
|
|
452
437
|
* @param {STEVE_WATCH_EVENT_PARAMS} event
|
|
453
438
|
*/
|
|
454
439
|
watchEvent(ctx, {
|
|
455
|
-
event =
|
|
440
|
+
event = STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
456
441
|
id,
|
|
457
442
|
callback,
|
|
458
443
|
/**
|
|
@@ -460,26 +445,27 @@ const sharedActions = {
|
|
|
460
445
|
*/
|
|
461
446
|
params
|
|
462
447
|
}) {
|
|
463
|
-
if (!
|
|
464
|
-
console.error(`Unknown event type "${ event }", only ${ Object.keys(
|
|
448
|
+
if (!ctx.getters.listenerManager.isSupportedEventType(event)) {
|
|
449
|
+
console.error(`Unknown event type "${ event }", only ${ Object.keys(ctx.getters.listenerManager.supportedEventTypes).join(',') } are supported`); // eslint-disable-line no-console
|
|
465
450
|
|
|
466
451
|
return;
|
|
467
452
|
}
|
|
468
453
|
|
|
469
|
-
|
|
470
|
-
|
|
454
|
+
ctx.getters.listenerManager.addEventListenerCallback({
|
|
455
|
+
callback,
|
|
456
|
+
args: {
|
|
457
|
+
event, params, id
|
|
458
|
+
}
|
|
459
|
+
});
|
|
471
460
|
|
|
472
|
-
|
|
473
|
-
listener = {
|
|
474
|
-
params,
|
|
475
|
-
callbacks: { }
|
|
476
|
-
};
|
|
477
|
-
listeners[event].push(listener);
|
|
478
|
-
}
|
|
461
|
+
const hasStandardWatch = ctx.getters.listenerManager.hasStandardWatch({ params });
|
|
479
462
|
|
|
480
|
-
if (!
|
|
481
|
-
|
|
482
|
-
ctx.dispatch('watch',
|
|
463
|
+
if (!hasStandardWatch) {
|
|
464
|
+
// If there's nothing to piggy back on... start a watch to do so.
|
|
465
|
+
ctx.dispatch('watch', {
|
|
466
|
+
...params,
|
|
467
|
+
standardWatch: false // Ensure that we don't treat this as a standard watch
|
|
468
|
+
});
|
|
483
469
|
}
|
|
484
470
|
},
|
|
485
471
|
|
|
@@ -488,24 +474,26 @@ const sharedActions = {
|
|
|
488
474
|
* @param {STEVE_UNWATCH_EVENT_PARAMS} event
|
|
489
475
|
*/
|
|
490
476
|
unwatchEvent(ctx, {
|
|
491
|
-
event =
|
|
477
|
+
event = STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
492
478
|
id,
|
|
493
479
|
/**
|
|
494
480
|
* of type @STEVE_WATCH_PARAMS
|
|
495
481
|
*/
|
|
496
482
|
params
|
|
497
483
|
}) {
|
|
498
|
-
if (!
|
|
484
|
+
if (!ctx.getters.listenerManager.isSupportedEventType(event)) {
|
|
499
485
|
console.info(`Attempted to unwatch for an event "${ event }" but it had no watchers`); // eslint-disable-line no-console
|
|
500
486
|
|
|
501
487
|
return;
|
|
502
488
|
}
|
|
503
489
|
|
|
504
|
-
|
|
490
|
+
ctx.getters.listenerManager.removeEventListenerCallback({
|
|
491
|
+
event, params, id
|
|
492
|
+
});
|
|
505
493
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
494
|
+
// Unwatch the underlying standard watch
|
|
495
|
+
// Note - If we were piggybacking on a watch that previously existed we won't unwatch it
|
|
496
|
+
ctx.dispatch('unwatch', params);
|
|
509
497
|
},
|
|
510
498
|
|
|
511
499
|
/**
|
|
@@ -517,7 +505,7 @@ const sharedActions = {
|
|
|
517
505
|
state.debugSocket && console.info(`Watch Request [${ getters.storeName }]`, JSON.stringify(params)); // eslint-disable-line no-console
|
|
518
506
|
let {
|
|
519
507
|
// eslint-disable-next-line prefer-const
|
|
520
|
-
type, selector, id, revision, namespace, stop, force, mode
|
|
508
|
+
type, selector, id, revision, namespace, stop, force, mode, standardWatch = true
|
|
521
509
|
} = params;
|
|
522
510
|
|
|
523
511
|
namespace = acceptOrRejectSocketMessage.subscribeNamespace(namespace);
|
|
@@ -562,10 +550,6 @@ const sharedActions = {
|
|
|
562
550
|
return;
|
|
563
551
|
}
|
|
564
552
|
|
|
565
|
-
if (!stop) {
|
|
566
|
-
dispatch('unwatchIncompatible', messageMeta);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
553
|
// Watch errors mean we make a http request to get latest revision (which is still missing) and try to re-watch with it...
|
|
570
554
|
// etc
|
|
571
555
|
if (typeof revision === 'undefined') {
|
|
@@ -618,6 +602,12 @@ const sharedActions = {
|
|
|
618
602
|
return;
|
|
619
603
|
}
|
|
620
604
|
|
|
605
|
+
if (!stop && standardWatch) {
|
|
606
|
+
// Track that this watch is just a normal one, not one kicked off by listeners
|
|
607
|
+
// This helps us keep the watch going (for listeners) instead of in unwatch just stopping it
|
|
608
|
+
getters.listenerManager.setStandardWatch({ standardWatch: true, args: { event: msg.mode, params: msg } });
|
|
609
|
+
}
|
|
610
|
+
|
|
621
611
|
return dispatch('send', msg);
|
|
622
612
|
},
|
|
623
613
|
|
|
@@ -642,6 +632,21 @@ const sharedActions = {
|
|
|
642
632
|
};
|
|
643
633
|
|
|
644
634
|
const unwatch = (obj) => {
|
|
635
|
+
// Has this normal watch got listeners? If so
|
|
636
|
+
const hasStandardWatch = ctx.getters.listenerManager.hasStandardWatch({ params: obj });
|
|
637
|
+
const watchHasListeners = ctx.getters.listenerManager.hasEventListeners({ params: obj });
|
|
638
|
+
|
|
639
|
+
if (hasStandardWatch) {
|
|
640
|
+
// If we have listeners for this watch... make sure it knows there's now no root standard watch
|
|
641
|
+
ctx.getters.listenerManager.setStandardWatch({ standardWatch: false, args: { params: obj } });
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (watchHasListeners) {
|
|
645
|
+
// Does this watch have listeners? if so we shouldn't stop it (they still need it)
|
|
646
|
+
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
645
650
|
if (getters['watchStarted'](obj)) {
|
|
646
651
|
// Set that we don't want to watch this type
|
|
647
652
|
// Otherwise, the dispatch to unwatch below will just cause a re-watch when we
|
|
@@ -651,45 +656,28 @@ const sharedActions = {
|
|
|
651
656
|
// Make sure anything in the pending queue for the type is removed, since we've now removed the type
|
|
652
657
|
commit('clearFromQueue', type);
|
|
653
658
|
}
|
|
654
|
-
// Ensure anything pinging in the background is stopped
|
|
655
|
-
backOff.resetPrefix(getters.backOffId(obj));
|
|
656
659
|
};
|
|
657
660
|
|
|
661
|
+
const objKey = keyForSubscribe(obj);
|
|
662
|
+
const reset = [];
|
|
663
|
+
|
|
658
664
|
if (isAdvancedWorker(ctx)) {
|
|
659
665
|
dispatch('watch', obj); // Ask the backend to stop watching the type
|
|
660
666
|
} else if (all) {
|
|
661
|
-
getters['watchesOfType'](type)
|
|
662
|
-
unwatch({ ...obj, stop: true });
|
|
663
|
-
});
|
|
667
|
+
reset.push(...getters['watchesOfType'](type));
|
|
664
668
|
} else if (getters['watchStarted'](obj)) {
|
|
665
|
-
|
|
669
|
+
reset.push(obj);
|
|
666
670
|
}
|
|
667
|
-
}
|
|
668
|
-
},
|
|
669
671
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const watchesOfType = getters.watchesOfType(messageMeta.type);
|
|
678
|
-
|
|
679
|
-
watchesOfType
|
|
680
|
-
.filter((entry) => shouldUnwatchIncompatible(messageMeta, entry.mode))
|
|
681
|
-
.forEach((entry) => {
|
|
682
|
-
dispatch('unwatch', entry);
|
|
672
|
+
reset.forEach((obj) => {
|
|
673
|
+
unwatch(obj);
|
|
674
|
+
// Ensure anything pinging in the background is stopped
|
|
675
|
+
dispatch('resetWatchBackOff', {
|
|
676
|
+
type,
|
|
677
|
+
compareWatches: (entry) => objKey === keyForSubscribe(entry)
|
|
678
|
+
});
|
|
683
679
|
});
|
|
684
|
-
|
|
685
|
-
// Step 2 - Clear inError state for incompatible watches (these won't appear in watchesOfType / state.started)
|
|
686
|
-
// (important for the backoff case... for example backoff request to find would overwrite findPage res if executed after nav from detail to list)
|
|
687
|
-
const inErrorOfType = Object.values(state.inError || {})
|
|
688
|
-
.filter((error) => error.obj.type === messageMeta.type);
|
|
689
|
-
|
|
690
|
-
inErrorOfType
|
|
691
|
-
.filter((error) => shouldUnwatchIncompatible(messageMeta, error.obj.mode))
|
|
692
|
-
.forEach((error) => clearInError({ getters, commit }, error));
|
|
680
|
+
}
|
|
693
681
|
},
|
|
694
682
|
|
|
695
683
|
/**
|
|
@@ -704,7 +692,7 @@ const sharedActions = {
|
|
|
704
692
|
if (resetStarted && state.started?.length) {
|
|
705
693
|
let entries = state.started;
|
|
706
694
|
|
|
707
|
-
if (type) { // Filter out ones for types we're no interested in
|
|
695
|
+
if (type || compareWatches) { // Filter out ones for types we're no interested in
|
|
708
696
|
entries = entries
|
|
709
697
|
.filter((obj) => compareWatches ? compareWatches(obj) : obj.type === type);
|
|
710
698
|
}
|
|
@@ -718,7 +706,7 @@ const sharedActions = {
|
|
|
718
706
|
// however resource.stop clears `started` and we need the settings to persist over start-->error-->stop-->start cycles
|
|
719
707
|
let entries = Object.values(state.inError || {});
|
|
720
708
|
|
|
721
|
-
if (type) { // Filter out ones for types we're no interested in
|
|
709
|
+
if (type || compareWatches) { // Filter out ones for types we're no interested in
|
|
722
710
|
entries = entries
|
|
723
711
|
.filter((error) => compareWatches ? compareWatches(error.obj) : error.obj.type === type);
|
|
724
712
|
}
|
|
@@ -817,6 +805,7 @@ const defaultActions = {
|
|
|
817
805
|
|
|
818
806
|
if ( getters.schemaFor(entry.type) ) {
|
|
819
807
|
commit('setWatchStopped', entry);
|
|
808
|
+
// Delete the cached socket revision, forcing the watch to get latest revision from cached resources instead
|
|
820
809
|
delete entry.revision;
|
|
821
810
|
promises.push(dispatch('watch', entry));
|
|
822
811
|
}
|
|
@@ -901,11 +890,7 @@ const defaultActions = {
|
|
|
901
890
|
}
|
|
902
891
|
|
|
903
892
|
// Should any listeners be notified of this request for them to kick off their own event handling?
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
if (listener) {
|
|
907
|
-
Object.values(listener.callbacks).forEach((cb) => cb());
|
|
908
|
-
}
|
|
893
|
+
getters.listenerManager.triggerEventListener({ event: STEVE_WATCH_MODE.RESOURCE_CHANGES, params });
|
|
909
894
|
} else {
|
|
910
895
|
have = getters['all'](resourceType).slice();
|
|
911
896
|
|
|
@@ -1086,10 +1071,16 @@ const defaultActions = {
|
|
|
1086
1071
|
mode: msg.mode,
|
|
1087
1072
|
};
|
|
1088
1073
|
|
|
1074
|
+
// Unwatch watches that are incompatible with the new type
|
|
1075
|
+
// This is mainly to prevent the cache being polluted with resources that aren't compatible with it's aim
|
|
1076
|
+
// For instance if the store/cache for pods contains a namespace X and we watch another namespace Y... we don't want ns X resources added to cache
|
|
1077
|
+
|
|
1078
|
+
// Unwatch incompatible watches
|
|
1089
1079
|
state.started.filter((entry) => {
|
|
1090
1080
|
if (
|
|
1091
|
-
entry.type === newWatch.type &&
|
|
1092
|
-
entry.namespace !== newWatch.namespace
|
|
1081
|
+
(entry.type === newWatch.type) &&
|
|
1082
|
+
(entry.namespace !== newWatch.namespace) &&
|
|
1083
|
+
(!entry.mode && !newWatch.mode) // mode watches will be handled when they become an issue
|
|
1093
1084
|
) {
|
|
1094
1085
|
return true;
|
|
1095
1086
|
}
|
|
@@ -1178,7 +1169,25 @@ const defaultActions = {
|
|
|
1178
1169
|
commit('setWatchStopped', obj);
|
|
1179
1170
|
}
|
|
1180
1171
|
|
|
1181
|
-
|
|
1172
|
+
// Now re-watch
|
|
1173
|
+
const hasEventListeners = getters.listenerManager.hasEventListeners({ params: obj });
|
|
1174
|
+
const hasStandardWatch = getters.listenerManager.hasStandardWatch({ params: obj });
|
|
1175
|
+
|
|
1176
|
+
dispatch('watch', {
|
|
1177
|
+
...obj,
|
|
1178
|
+
// hasEventListeners && !hasStandardWatch ? false : true
|
|
1179
|
+
// if this watch isn't associated with a normal watch... (there are no listeners, or there are listeners but also a normal watch)
|
|
1180
|
+
standardWatch: !(hasEventListeners && !hasStandardWatch)
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
if (hasEventListeners) {
|
|
1184
|
+
// If there's event listeners always kick them off
|
|
1185
|
+
// - The re-watch associated with normal watches will watch from a revision from it's own cache
|
|
1186
|
+
// - The revision in that cache might be ahead of the state the listeners have, so the watch won't ping something for the listeners to trigger on
|
|
1187
|
+
// - so to work around this whenever we start the watches again trigger off the changes for it
|
|
1188
|
+
// Improvement - we only do one event here (currently the only one supported), could expand to others
|
|
1189
|
+
getters.listenerManager.triggerEventListener({ event: STEVE_WATCH_EVENT_TYPES.CHANGES, params: obj });
|
|
1190
|
+
}
|
|
1182
1191
|
}
|
|
1183
1192
|
},
|
|
1184
1193
|
|
|
@@ -1210,6 +1219,15 @@ const defaultActions = {
|
|
|
1210
1219
|
}
|
|
1211
1220
|
}
|
|
1212
1221
|
|
|
1222
|
+
const havePage = ctx.getters['havePage'](type);
|
|
1223
|
+
|
|
1224
|
+
if (havePage) {
|
|
1225
|
+
console.warn(`Prevented watch \`resource.change\` data from polluting the cache for type "${ type }" (currently represents a page). To prevent any further issues the watch has been stopped.`, data); // eslint-disable-line no-console
|
|
1226
|
+
ctx.dispatch('unwatch', data);
|
|
1227
|
+
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1213
1231
|
queueChange(ctx, msg, true, 'Change');
|
|
1214
1232
|
|
|
1215
1233
|
const typeOption = ctx.rootGetters['type-map/optionsFor'](type);
|
|
@@ -1344,6 +1362,7 @@ const defaultMutations = {
|
|
|
1344
1362
|
clearTimeout(state.queueTimer);
|
|
1345
1363
|
state.deferredRequests = {};
|
|
1346
1364
|
state.queueTimer = null;
|
|
1365
|
+
state.socketListenerManager = new SteveWatchEventListenerManager(state.config.namespace);
|
|
1347
1366
|
},
|
|
1348
1367
|
|
|
1349
1368
|
clearFromQueue(state, type) {
|
|
@@ -1444,6 +1463,15 @@ const defaultGetters = {
|
|
|
1444
1463
|
|
|
1445
1464
|
return revision || null;
|
|
1446
1465
|
},
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Get the watch listener manager for this store
|
|
1469
|
+
*
|
|
1470
|
+
* Instance of @SteveWatchEventListenerManager . See it's description for more info
|
|
1471
|
+
*/
|
|
1472
|
+
listenerManager: (state) => {
|
|
1473
|
+
return state.socketListenerManager;
|
|
1474
|
+
},
|
|
1447
1475
|
};
|
|
1448
1476
|
|
|
1449
1477
|
export const actions = {
|