Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compatibility functions to support multiple server version #16

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
479 changes: 479 additions & 0 deletions pkg/harvester/components/ResourceDetail/index.vue

Large diffs are not rendered by default.

306 changes: 306 additions & 0 deletions pkg/harvester/components/ResourceList/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
<script>
import ResourceTable from '@shell/components/ResourceTable';
import Loading from '@shell/components/Loading';
import Masthead from '@shell/components/ResourceList/Masthead';
import ResourceLoadingIndicator from '@shell/components/ResourceList/ResourceLoadingIndicator';
import ResourceFetch from '@shell/mixins/resource-fetch';
import IconMessage from '@shell/components/IconMessage.vue';
import { ResourceListComponentName } from '@shell/components/ResourceList/resource-list.config';
import { PanelLocation, ExtensionPoint } from '@shell/core/types';
import ExtensionPanel from '@shell/components/ExtensionPanel';
import { sameContents } from '@shell/utils/array';

export default {
name: ResourceListComponentName,

components: {
Loading,
ResourceTable,
Masthead,
ResourceLoadingIndicator,
IconMessage,
ExtensionPanel
},
mixins: [ResourceFetch],

props: {
hasAdvancedFiltering: {
type: Boolean,
default: false
},
advFilterHideLabelsAsCols: {
type: Boolean,
default: false
},
advFilterPreventFilteringLabels: {
type: Boolean,
default: false
},
},

async fetch() {
const store = this.$store;
const resource = this.resource;

const schema = this.schema;

if ( this.hasListComponent ) {
// If you provide your own list then call its fetch
const importer = this.listComponent;

const component = await importer.__asyncLoader();

if ( component?.typeDisplay ) {
this.customTypeDisplay = component.typeDisplay.apply(this);
}

// If your list page has a fetch then it's responsible for populating rows itself
if ( component?.fetch ) {
this.componentWillFetch = true;
}

// If the custom component supports it, ask it what resources it loads, so we can
// use the incremental loading indicator when enabled
if (component?.$loadingResources) {
const { loadResources, loadIndeterminate } = component?.$loadingResources(this.$route, this.$store);

this.loadResources = loadResources || [resource];
this.loadIndeterminate = loadIndeterminate || false;
}
}

if ( !this.componentWillFetch ) {
if ( !schema ) {
store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceListNotFound', { resource }, true)));

return;
}

// See comment for `namespaceFilter` and `pagination` watchers, skip fetch if we're not ready yet... and something is going to call fetch later on
if (!this.namespaceFilterRequired && (!this.canPaginate || this.refreshFlag)) {
await this.$fetchType(resource);
}
}
},

data() {
const getters = this.$store.getters;
const params = { ...this.$route.params };
const resource = params.resource;

const hasListComponent = !!getters['harvester/getCustomList'](resource);

const inStore = getters['currentStore'](resource);
const schema = getters[`${ inStore }/schemaFor`](resource);

const showMasthead = getters[`type-map/optionsFor`](resource).showListMasthead;

return {
inStore,
schema,
hasListComponent,
showMasthead: showMasthead === undefined ? true : showMasthead,
resource,
extensionType: ExtensionPoint.PANEL,
extensionLocation: PanelLocation.RESOURCE_LIST,
loadResources: [resource], // List of resources that will be loaded, this could be many (`Workloads`)
/**
* Will the custom component handle the fetch of resources....
* or will this instance fetch resources
*/
componentWillFetch: false,
// manual refresh
manualRefreshInit: false,
watch: false,
force: false,
// Provided by fetch later
customTypeDisplay: null,
// incremental loading
loadIndeterminate: false,
// query param for simple filtering
useQueryParamsForSimpleFiltering: true,
};
},

computed: {
headers() {
if ( this.hasListComponent || !this.schema ) {
// Custom lists figure out their own headers
return [];
}

return this.$store.getters['type-map/headersFor'](this.schema, this.canPaginate);
},

groupBy() {
return this.$store.getters['type-map/groupByFor'](this.schema);
},

showIncrementalLoadingIndicator() {
return this.perfConfig?.incrementalLoading?.enabled;
},

},

watch: {

/**
* When a NS filter is required and the user selects a different one, kick off a new set of API requests
*
* ResourceList has two modes
* 1) ResourceList component handles API request to fetch resources
* 2) Custom list component handles API request to fetch resources
*
* This covers case 1
*/
namespaceFilter(neu, old) {
if (neu && !this.componentWillFetch) {
if (sameContents(neu, old)) {
return;
}

this.$fetchType(this.resource);
}
},

/**
* When a pagination is required and the user changes page / sort / filter, kick off a new set of API requests
*
* ResourceList has two modes
* 1) ResourceList component handles API request to fetch resources
* 2) Custom list component handles API request to fetch resources
*
* This covers case 1
*/
pagination(neu, old) {
if (neu && !this.componentWillFetch && !this.paginationEqual(neu, old)) {
this.$fetchType(this.resource);
}
},

/**
* Monitor the rows to ensure deleting the last entry in a server-side paginated page doesn't
* result in an empty page
*/
rows(neu) {
if (!this.pagination) {
return;
}

if (this.pagination.page > 1 && neu.length === 0) {
this.setPagination({
...this.pagination,
page: this.pagination.page - 1
});
}
},
},

created() {
let listComponent = false;

const resource = this.$route.params.resource;

const customList = this.$store.getters['harvester/getCustomList'](resource);

if ( customList ) {
listComponent = this.$store.getters['type-map/importList'](customList);
}

this.listComponent = listComponent;
},
};
</script>

<template>
<IconMessage
v-if="namespaceFilterRequired"
:vertical="true"
:subtle="false"
icon="icon-filter_alt"
>
<template #message>
{{ t('resourceList.nsFiltering') }}
</template>
</IconMessage>
<IconMessage
v-else-if="paginationNsFilterRequired"
:vertical="true"
:subtle="false"
icon="icon-filter_alt"
>
<template #message>
{{ t('resourceList.nsFilteringGeneric') }}
</template>
</IconMessage>
<div
v-else
class="outlet"
>
<Masthead
v-if="showMasthead"
:type-display="customTypeDisplay"
:schema="schema"
:resource="resource"
:show-incremental-loading-indicator="showIncrementalLoadingIndicator"
:load-resources="loadResources"
:load-indeterminate="loadIndeterminate"
>
<template #extraActions>
<slot name="extraActions" />
</template>
</Masthead>
<!-- Extensions area -->
<ExtensionPanel
:resource="{}"
:type="extensionType"
:location="extensionLocation"
/>

<div
v-if="hasListComponent"
>
<component
:is="listComponent"
:incremental-loading-indicator="showIncrementalLoadingIndicator"
:rows="rows"
v-bind="$data"
/>
</div>
<ResourceTable
v-else
:schema="schema"
:rows="rows"
:alt-loading="canPaginate"
:loading="loading"
:headers="headers"
:group-by="groupBy"
:has-advanced-filtering="hasAdvancedFiltering"
:adv-filter-hide-labels-as-cols="advFilterHideLabelsAsCols"
:adv-filter-prevent-filtering-labels="advFilterPreventFilteringLabels"
:use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
:force-update-live-and-delayed="forceUpdateLiveAndDelayed"
:external-pagination-enabled="canPaginate"
:external-pagination-result="paginationResult"
@pagination-changed="paginationChanged"
/>
</div>
</template>

<style lang="scss" scoped>
.header {
position: relative;
}
H2 {
position: relative;
margin: 0 0 20px 0;
}
.filter{
line-height: 45px;
}
.right-action {
position: absolute;
top: 10px;
right: 10px;
}
</style>
38 changes: 38 additions & 0 deletions pkg/harvester/components/VersionSwitch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script>
import { serverVersion } from '@pkg/harvester/utils/server';

export default {
name: 'VersionSwitch',

props: {
component: {
type: String,
required: true,
}
},

data() {
const version = serverVersion(this.$store.getters);

let currentComponent = null;

try {
currentComponent = require(`./${ this.component }-${ version }.vue`).default;
} catch {
currentComponent = require(`./${ this.component }.vue`).default;
}

return { currentComponent };
},

};
</script>

<template>
<div>
<component
:is="currentComponent"
v-bind="$attrs"
/>
</div>
</template>
8 changes: 8 additions & 0 deletions pkg/harvester/detail/harvesterhci.io.volume.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>

export default { name: 'HarvesterVolumeDetails' };
</script>

<template>
<span>volume details</span>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>

export default { name: 'HarvesterVMDetails' };
</script>

<template>
<span>pippo details</span>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>

export default { name: 'HarvesterVMCreate' };
</script>

<template>
<span>pippo edit</span>
</template>
Loading
Loading