mirror of https://github.com/rancher/dashboard.git
302 lines
10 KiB
JavaScript
302 lines
10 KiB
JavaScript
import { mapGetters } from 'vuex';
|
|
import { COUNT, MANAGEMENT } from '@shell/config/types';
|
|
import { SETTING, DEFAULT_PERF_SETTING } from '@shell/config/settings';
|
|
import ResourceFetchNamespaced from '@shell/mixins/resource-fetch-namespaced';
|
|
import ResourceFetchApiPagination from '@shell/mixins/resource-fetch-api-pagination';
|
|
import perfSettingsUtils from '@shell/utils/perf-setting.utils';
|
|
|
|
// Number of pages to fetch when loading incrementally
|
|
const PAGES = 4;
|
|
|
|
export default {
|
|
|
|
mixins: [
|
|
ResourceFetchNamespaced,
|
|
ResourceFetchApiPagination
|
|
],
|
|
|
|
inheritAttrs: false,
|
|
|
|
data() {
|
|
// fetching the settings related to manual refresh from global settings
|
|
const perfSetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_PERFORMANCE);
|
|
let perfConfig = {};
|
|
|
|
if (perfSetting && perfSetting.value) {
|
|
try {
|
|
perfConfig = JSON.parse(perfSetting.value);
|
|
} catch (e) {
|
|
console.warn('ui-performance setting contains invalid data'); // eslint-disable-line no-console
|
|
}
|
|
} else {
|
|
perfConfig = DEFAULT_PERF_SETTING;
|
|
}
|
|
|
|
// Normally owner components supply `resource` and `inStore` as part of their data, however these are needed here before parent data runs
|
|
// So set up both here
|
|
const params = { ...this.$route.params };
|
|
const resource = params.resource || this.schema?.id; // Resource can either be on a page showing single list, or a page of a resource showing a list of another resource
|
|
const inStore = this.$store.getters['currentStore'](resource);
|
|
|
|
return {
|
|
inStore,
|
|
perfConfig,
|
|
init: false,
|
|
multipleResources: [],
|
|
loadResources: [resource],
|
|
// manual refresh vars
|
|
hasManualRefresh: false,
|
|
watch: true,
|
|
isTooManyItemsToAutoUpdate: false,
|
|
force: false,
|
|
// incremental loading vars
|
|
incremental: false,
|
|
fetchedResourceType: [],
|
|
paginating: null,
|
|
isFirstLoad: true,
|
|
};
|
|
},
|
|
|
|
beforeUnmount() {
|
|
// make sure this only runs once, for the initialized instance
|
|
if (this.init) {
|
|
// clear up the store to make sure we aren't storing anything that might interfere with the next rendered list view
|
|
this.$store.dispatch('resource-fetch/clearData');
|
|
|
|
this.fetchedResourceType.forEach((item) => {
|
|
this.$store.dispatch(`${ item.currStore }/incrementLoadCounter`, item.type);
|
|
});
|
|
}
|
|
},
|
|
|
|
props: {
|
|
/**
|
|
* Add additional filtering to the rows
|
|
*
|
|
* Should only be used when we have all results, otherwise we're filtering a page which already has been filtered...
|
|
*/
|
|
localFilter: {
|
|
type: Function,
|
|
default: null,
|
|
},
|
|
|
|
/**
|
|
* Add additional filtering to the pagination api request
|
|
*/
|
|
apiFilter: {
|
|
type: Function,
|
|
default: null,
|
|
},
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters({ refreshFlag: 'resource-fetch/refreshFlag' }),
|
|
|
|
rows() {
|
|
const currResource = this.fetchedResourceType.find((item) => item.type === this.resource);
|
|
|
|
if (currResource) {
|
|
const rows = this.$store.getters[`${ currResource.currStore }/all`](this.resource);
|
|
|
|
if (this.canPaginate) {
|
|
if (this.havePaginated) {
|
|
return rows;
|
|
}
|
|
} else {
|
|
return this.localFilter ? this.localFilter(rows) : rows;
|
|
}
|
|
}
|
|
|
|
return [];
|
|
},
|
|
|
|
loading() {
|
|
if (this.canPaginate) {
|
|
return this.paginating === null ? true : this.paginating;
|
|
}
|
|
|
|
return this.rows.length ? false : this.$fetchState.pending;
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
async refreshFlag(neu) {
|
|
// this is where the data assignment will trigger the update of the list view...
|
|
if (this.init && neu) {
|
|
await this.$fetch();
|
|
if (this.clearSelection) {
|
|
this.clearSelection();
|
|
}
|
|
if (this.canPaginate && this.fetchPageSecondaryResources) {
|
|
this.fetchPageSecondaryResources({
|
|
canPaginate: this.canPaginate, force: true, page: this.rows, pagResult: this.paginationResult
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
loading(newValue, oldValue) {
|
|
if (oldValue && !newValue) {
|
|
this.isFirstLoad = false;
|
|
}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
// this defines all the flags needed for the mechanism
|
|
// to work. They should be defined based on the main list view
|
|
// resource that is to be displayed. The secondary resources
|
|
// fetched should follow what was defined (if it is manual and/or incremental)
|
|
$initializeFetchData(type, multipleResources = [], storeType) {
|
|
if (!this.init) {
|
|
const currStore = storeType || this.$store.getters['currentStore']();
|
|
|
|
this.__gatherResourceFetchData(type, multipleResources, currStore);
|
|
|
|
// make sure after init that, if we have a manual refresh, we always set the force = true
|
|
if (!this.watch) {
|
|
this.force = true;
|
|
}
|
|
|
|
if (this.isTooManyItemsToAutoUpdate) {
|
|
this.hasManualRefresh = true;
|
|
}
|
|
}
|
|
},
|
|
// data fetching for the mechanism
|
|
$fetchType(type, multipleResources = [], storeType) {
|
|
const currStore = storeType || this.$store.getters['currentStore']();
|
|
|
|
this.$initializeFetchData(type, multipleResources, currStore);
|
|
|
|
if (!this.fetchedResourceType.find((item) => item.type === type)) {
|
|
this.fetchedResourceType.push({
|
|
type,
|
|
currStore
|
|
});
|
|
}
|
|
|
|
const schema = this.$store.getters[`${ currStore }/schemaFor`](type);
|
|
|
|
if (this.canPaginate) {
|
|
if (!this.pagination) {
|
|
// This is the initial fetchType made when resource lists are created...
|
|
// when pagination is enabled we want to wait for the correct set of initial pagination settings to make the call
|
|
return;
|
|
}
|
|
const opt = {
|
|
hasManualRefresh: this.hasManualRefresh,
|
|
pagination: { ...this.pagination },
|
|
force: this.paginating !== null // Fix for manual refresh (before ripped out).
|
|
};
|
|
|
|
if (this.apiFilter) {
|
|
opt.paginating = this.apiFilter(opt.pagination);
|
|
}
|
|
|
|
this['paginating'] = true;
|
|
|
|
const that = this;
|
|
|
|
return this.$store.dispatch(`${ currStore }/findPage`, {
|
|
type,
|
|
opt
|
|
})
|
|
.finally(() => (that['paginating'] = false));
|
|
}
|
|
|
|
let incremental = null;
|
|
|
|
if (this.incremental) {
|
|
const resourceCount = this.__getCountForResources([type], this.namespaceFilter, currStore);
|
|
|
|
incremental = {
|
|
quickLoadCount: 100,
|
|
resourcesPerIncrement: Math.ceil(resourceCount / PAGES),
|
|
increments: PAGES,
|
|
pageByNumber: this.$store.getters[`${ this.inStore }/paginationEnabled`]?.()
|
|
};
|
|
}
|
|
|
|
const opt = { // Of type ActionFindAllArgs
|
|
incremental,
|
|
watch: this.watch,
|
|
force: this.force,
|
|
hasManualRefresh: this.hasManualRefresh
|
|
};
|
|
|
|
if (schema?.attributes?.namespaced) { // Is this specific resource namespaced (could be primary or secondary resource)?
|
|
opt.namespaced = this.namespaceFilter; // namespaceFilter will only be populated if applicable for primary resource
|
|
}
|
|
|
|
return this.$store.dispatch(`${ currStore }/findAll`, {
|
|
type,
|
|
opt
|
|
});
|
|
},
|
|
|
|
__getCountForResources(resourceNames, namespace, storeType) {
|
|
const currStore = storeType || this.$store.getters['currentStore']();
|
|
|
|
return resourceNames.reduce((res, type) => res + this.__getCountForResource(type, namespace, currStore), 0);
|
|
},
|
|
|
|
__getCountForResource(resourceName, namespace, storeType) {
|
|
const currStore = storeType || this.$store.getters['currentStore']();
|
|
|
|
const resourceCounts = this.$store.getters[`${ currStore }/all`](COUNT)[0]?.counts[`${ resourceName }`]; // NB `rancher` store behaves differently, lacks counts but has resource
|
|
const resourceCount = namespace && resourceCounts?.namespaces ? resourceCounts?.namespaces[namespace]?.count : resourceCounts?.summary?.count;
|
|
|
|
return resourceCount || 0;
|
|
},
|
|
|
|
__gatherResourceFetchData(resourceName, multipleResources, currStore) {
|
|
// flag to prevent a first data update being triggered from the requestData watcher
|
|
this.init = true;
|
|
|
|
// manual refresh settings config
|
|
const manualDataRefreshEnabled = perfSettingsUtils.manualRefreshUtils.isEnabled(this.calcCanPaginate(), this.perfConfig);
|
|
const manualDataRefreshThreshold = parseInt(this.perfConfig?.manualRefresh?.threshold || '0', 10);
|
|
|
|
// incremental loading settings config
|
|
const incrementalLoadingEnabled = perfSettingsUtils.incrementalLoadingUtils.isEnabled(this.calcCanPaginate(), this.perfConfig);
|
|
const incrementalLoadingThreshold = parseInt(this.perfConfig?.incrementalLoading?.threshold || '0', 10);
|
|
|
|
// other vars
|
|
this.multipleResources = multipleResources;
|
|
let resourceCount = 0;
|
|
|
|
// manual refresh vars
|
|
let watch = true;
|
|
let isTooManyItemsToAutoUpdate = false;
|
|
|
|
// incremental loading vars
|
|
let incremental = false;
|
|
|
|
// get resource counts
|
|
const resourcesForCount = this.multipleResources.length ? this.multipleResources : [resourceName];
|
|
|
|
resourceCount = this.__getCountForResources(resourcesForCount, this.namespaceFilter, currStore);
|
|
|
|
// manual refresh check
|
|
if (manualDataRefreshEnabled && resourceCount >= manualDataRefreshThreshold) {
|
|
watch = false;
|
|
isTooManyItemsToAutoUpdate = true;
|
|
} else if (this.canPaginate && this.isPaginationManualRefreshEnabled) {
|
|
isTooManyItemsToAutoUpdate = true;
|
|
}
|
|
|
|
// incremental loading check
|
|
incremental = incrementalLoadingEnabled && incrementalLoadingThreshold > 0 && resourceCount >= incrementalLoadingThreshold;
|
|
|
|
// pass on the flag that controls the appearance of the manual refresh button on the sortable table
|
|
this.$store.dispatch('resource-fetch/updateIsTooManyItems', isTooManyItemsToAutoUpdate);
|
|
|
|
// set vars on mixin to be used on $fetchType
|
|
this.watch = watch;
|
|
this.isTooManyItemsToAutoUpdate = isTooManyItemsToAutoUpdate;
|
|
this.incremental = incremental;
|
|
},
|
|
},
|
|
};
|