From 8c474ff64c54455c1702ae10093b8650aebff82f Mon Sep 17 00:00:00 2001 From: Richard Cox <18697775+richard-cox@users.noreply.github.com> Date: Wed, 6 Aug 2025 08:43:23 +0100 Subject: [PATCH] Merge pull request #15011 from richard-cox/fix-resource-detail-updates Fix general and specific resource detail watch issues --- shell/detail/workload/index.vue | 10 ++++ shell/mixins/resource-fetch-api-pagination.js | 7 ++- shell/models/workload.js | 4 +- shell/plugins/dashboard-store/actions.js | 52 ++++++++++++------- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/shell/detail/workload/index.vue b/shell/detail/workload/index.vue index cffeeea034..06bd6c1d71 100644 --- a/shell/detail/workload/index.vue +++ b/shell/detail/workload/index.vue @@ -311,6 +311,16 @@ export default { this.matchingIngresses = matchingIngresses; } + }, + + watch: { + async 'value.jobRelationships.length'(neu, old) { + // If there are MORE jobs ensure we go out and fetch them (changes and removals are tracked by watches) + if (neu > old) { + // We don't need to worry about spam, this won't be called often and it will be infrequent + await this.value.matchingJobs(); + } + } } }; diff --git a/shell/mixins/resource-fetch-api-pagination.js b/shell/mixins/resource-fetch-api-pagination.js index d1ed636249..f2b7ba86bc 100644 --- a/shell/mixins/resource-fetch-api-pagination.js +++ b/shell/mixins/resource-fetch-api-pagination.js @@ -352,7 +352,7 @@ export default { } }, - unmounted() { + async beforeUnmount() { if (this.havePaginated) { // of type @STEVE_WATCH_PARAMS const watchArgs = { @@ -360,9 +360,8 @@ export default { mode: STEVE_WATCH_MODE.RESOURCE_CHANGES, }; - this.$store.dispatch(`${ this.overrideInStore || this.inStore }/forgetType`, this.resource, (watchParams) => { - return watchParams.type === watchArgs.type && - watchParams.mode === watchArgs.type.mode; + await this.$store.dispatch(`${ this.overrideInStore || this.inStore }/forgetType`, this.resource, (watchParams) => { + return watchParams.type === watchArgs.type && watchParams.mode === watchArgs.type.mode; }); } } diff --git a/shell/models/workload.js b/shell/models/workload.js index b1ba5dd8dc..a98995b25e 100644 --- a/shell/models/workload.js +++ b/shell/models/workload.js @@ -1,7 +1,7 @@ import { findBy, insertAt } from '@shell/utils/array'; import { CATTLE_PUBLIC_ENDPOINTS } from '@shell/config/labels-annotations'; import { WORKLOAD_TYPES, SERVICE, POD } from '@shell/config/types'; -import { get, set } from '@shell/utils/object'; +import { set } from '@shell/utils/object'; import day from 'dayjs'; import { convertSelectorObj, parse } from '@shell/utils/selector'; import { SEPARATOR } from '@shell/config/workload'; @@ -632,7 +632,7 @@ export default class Workload extends WorkloadService { return undefined; } - return (get(this, 'metadata.relationships') || []).filter((relationship) => relationship.toType === WORKLOAD_TYPES.JOB); + return this.metadata?.relationships?.filter((relationship) => relationship.toType === WORKLOAD_TYPES.JOB) || []; } /** diff --git a/shell/plugins/dashboard-store/actions.js b/shell/plugins/dashboard-store/actions.js index 0a96b02faa..590daa05ba 100644 --- a/shell/plugins/dashboard-store/actions.js +++ b/shell/plugins/dashboard-store/actions.js @@ -79,6 +79,29 @@ const findAllGetter = (getters, type, opt) => { return opt.namespaced ? getters.matching(type, null, opt.namespaced, { skipSelector: true }) : getters.all(type); }; +const createFindWatchArg = ({ + type, id, opt, res +}) => { + const revision = typeof opt.revision !== 'undefined' ? opt.revision : res?.metadata?.resourceVersion; + const watchMsg = { + type, + id, + // Although not used by sockets, we need this for when resyncWatch calls find... which needs namespace to construct the url + namespace: opt.namespaced, + revision: revision || '', + force: opt.forceWatch === true, + }; + + const idx = id.indexOf('/'); + + if ( idx > 0 ) { + watchMsg.namespace = id.substr(0, idx); + watchMsg.id = id.substr(idx + 1); + } + + return watchMsg; +}; + export default { request() { throw new Error('Not Implemented'); @@ -657,6 +680,12 @@ export default { out = getters.byId(type, id); if ( out ) { + if ( opt.watch !== false ) { + dispatch('watch', createFindWatchArg({ + type, id, opt, res: undefined + })); + } + return out; } } @@ -669,26 +698,9 @@ export default { await dispatch('load', { data: res }); if ( opt.watch !== false ) { - const watchMsg = { - type, - id, - // Although not used by sockets, we need this for when resyncWatch calls find... which needs namespace to construct the url - namespace: opt.namespaced, - // Override the revision. Used in cases where we need to avoid using the resource's own revision which would be `too old`. - // For the above case opt.revision will be `null`. If left as `undefined` the subscribe mechanism will try to determine a revision - // from resources in store (which would be this one, with the too old revision) - revision: typeof opt.revision !== 'undefined' ? opt.revision : res?.metadata?.resourceVersion, - force: opt.forceWatch === true, - }; - - const idx = id.indexOf('/'); - - if ( idx > 0 ) { - watchMsg.namespace = id.substr(0, idx); - watchMsg.id = id.substr(idx + 1); - } - - dispatch('watch', watchMsg); + dispatch('watch', createFindWatchArg({ + type, id, opt, res + })); } out = getters.byId(type, id);