@rancher/shell 3.0.12-rc.5 → 3.0.12-rc.6
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/apis/intf/resources-api/resource-base.ts +42 -2
- package/apis/intf/resources-api/resource-instance.ts +101 -0
- package/apis/intf/resources-api/resources-api.ts +177 -23
- package/apis/intf/resources.ts +8 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
- package/apis/resources/resources-api-class.ts +232 -43
- package/assets/images/providers/traefik.png +0 -0
- package/assets/translations/en-us.yaml +10 -4
- package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
- package/chart/rancher-monitoring-dashboards/index.vue +155 -0
- package/cloud-credential/__tests__/generic.test.ts +132 -0
- package/cloud-credential/generic.vue +33 -3
- package/components/EtcdInfoBanner.vue +2 -12
- package/components/GrafanaDashboard.vue +2 -8
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
- package/components/Resource/Detail/Masthead/index.vue +8 -3
- package/components/form/Security.vue +1 -1
- package/components/formatter/Translate.vue +22 -1
- package/components/nav/Group.vue +5 -10
- package/components/nav/TopLevelMenu.vue +1 -30
- package/components/nav/__tests__/Group.test.ts +61 -0
- package/config/labels-annotations.js +1 -0
- package/config/table-headers.js +5 -2
- package/core/__tests__/plugin-products-apply.test.ts +787 -0
- package/core/__tests__/plugin-products-extend.test.ts +189 -0
- package/core/__tests__/plugin-products-helpers.test.ts +81 -78
- package/core/__tests__/plugin-products-new.test.ts +389 -0
- package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
- package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
- package/core/plugin-products-base.ts +201 -93
- package/core/plugin-products-extending.ts +1 -1
- package/core/plugin-products-external.ts +380 -0
- package/core/plugin-products-helpers.ts +28 -24
- package/core/plugin-products-internal.ts +207 -0
- package/core/plugin-products-top-level.ts +10 -5
- package/core/plugin-products-type-guards.ts +17 -6
- package/core/plugin-products.ts +5 -7
- package/core/plugin-types.ts +1 -389
- package/core/plugin.ts +10 -11
- package/core/types.ts +6 -202
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +0 -2
- package/edit/auth/AuthProviderWarningBanners.vue +12 -0
- package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
- package/edit/auth/__tests__/azuread.test.ts +1 -0
- package/edit/auth/__tests__/github.test.ts +1 -0
- package/edit/auth/__tests__/oidc.test.ts +1 -0
- package/edit/auth/__tests__/saml.test.ts +1 -0
- package/edit/auth/ldap/__tests__/index.test.ts +1 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
- package/list/management.cattle.io.user.vue +24 -5
- package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
- package/machine-config/__tests__/generic.test.ts +163 -0
- package/machine-config/components/EC2Networking.vue +31 -7
- package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
- package/machine-config/generic.vue +32 -5
- package/mixins/resource-fetch.js +1 -1
- package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
- package/models/pod.js +12 -10
- package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
- package/package.json +1 -1
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +110 -0
- package/pages/c/_cluster/apps/charts/install.vue +29 -0
- package/pages/c/_cluster/auth/config/index.vue +11 -4
- package/pages/c/_cluster/monitoring/index.vue +6 -1
- package/plugins/dashboard-store/resource-class.js +10 -3
- package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
- package/plugins/steve/steve-class.js +43 -0
- package/plugins/steve/subscribe.js +11 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
- package/store/__tests__/auth.test.ts +18 -0
- package/store/__tests__/notifications.test.ts +466 -1
- package/store/__tests__/ui-context.test.ts +255 -0
- package/store/auth.js +11 -1
- package/store/features.js +1 -0
- package/store/plugins.js +6 -0
- package/types/rancher/steve.api.ts +2 -2
- package/types/shell/index.d.ts +58 -0
- package/types/store/dashboard-store.types.ts +2 -2
- package/types/store/type-map.ts +262 -1
- package/utils/async.ts +2 -0
- package/utils/grafana.js +2 -17
- package/utils/monitoring.js +38 -1
- package/core/__tests__/plugin-products.test.ts +0 -4694
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
import { IExtension } from '@shell/core/types';
|
|
2
|
-
import { ProductChild, ProductMetadata, ProductSinglePage } from '@shell/core/plugin-types';
|
|
3
2
|
import EmptyProductPage from '@shell/components/EmptyProductPage.vue';
|
|
4
3
|
import pluginProductsHelpers from '@shell/core/plugin-products-helpers';
|
|
5
4
|
import { BasePluginProduct } from '@shell/core/plugin-products-base';
|
|
6
5
|
import { isProductSinglePage } from '@shell/core/plugin-products-type-guards';
|
|
6
|
+
import { ProductChild, ProductMetadata, ProductMetadataSinglePage } from '@shell/core/plugin-products-external';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Represents a new top-level product being added by an extension
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
12
12
|
export class TopLevelPluginProduct extends BasePluginProduct {
|
|
13
|
+
protected product?: ProductMetadata | ProductMetadataSinglePage;
|
|
14
|
+
|
|
13
15
|
get isNewProduct(): boolean {
|
|
14
16
|
return true;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
constructor(plugin: IExtension, product: ProductMetadata |
|
|
18
|
-
super(
|
|
19
|
+
constructor(plugin: IExtension, product: ProductMetadata | ProductMetadataSinglePage | string, pages: ProductChild[]) {
|
|
20
|
+
super(pages);
|
|
19
21
|
|
|
20
22
|
// Convenience/bridge method: create a basic product from just a name string
|
|
21
23
|
if (typeof product === 'string') {
|
|
22
|
-
|
|
24
|
+
const emptyProduct: ProductMetadata = {
|
|
23
25
|
name: product,
|
|
24
26
|
label: product,
|
|
25
27
|
};
|
|
28
|
+
|
|
29
|
+
product = emptyProduct;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
let prodName = product.name;
|
|
@@ -45,8 +49,9 @@ export class TopLevelPluginProduct extends BasePluginProduct {
|
|
|
45
49
|
|
|
46
50
|
// If the product has a `component` field, then this is a single page product
|
|
47
51
|
if (isProductSinglePage(product)) {
|
|
52
|
+
const productSinglePage: ProductMetadataSinglePage = product as ProductMetadataSinglePage;
|
|
48
53
|
// Add the route to vue-router (here we go with the 'plain' layout for simple single page products)
|
|
49
|
-
const route = pluginProductsHelpers.generateTopLevelExtensionSimpleBaseRoute(this.name, { component:
|
|
54
|
+
const route = pluginProductsHelpers.generateTopLevelExtensionSimpleBaseRoute(this.name, { component: productSinglePage.component });
|
|
50
55
|
|
|
51
56
|
plugin.addRoute('plain', route);
|
|
52
57
|
} else if (this.config.length === 0) {
|
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ProductChild, ProductChildGroup, ProductMetadata,
|
|
3
|
-
ProductSinglePage, ProductChildCustomPage, ProductChildResourcePage
|
|
4
|
-
} from '@shell/core/plugin-types';
|
|
5
1
|
|
|
6
2
|
/**
|
|
7
3
|
* Type guard functions for discriminating union types
|
|
8
4
|
* @internal
|
|
9
5
|
*/
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
import {
|
|
8
|
+
ProductChild, ProductChildCustomPage, ProductChildGroup, ProductChildResourcePage, ProductMetadata, ProductMetadataSinglePage
|
|
9
|
+
} from '@shell/core/plugin-products-external';
|
|
10
|
+
import { ProductMetadataInternal } from '@shell/core/plugin-products-internal';
|
|
11
|
+
|
|
12
|
+
export function isProductConfigInternal(product: ProductMetadata | ProductMetadataSinglePage): product is ProductMetadataInternal {
|
|
13
|
+
const props = ['category', 'version'];
|
|
14
|
+
|
|
15
|
+
return props.some((prop) => prop in product);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isProductSinglePage(product: ProductMetadata | ProductMetadataSinglePage): product is ProductMetadataSinglePage {
|
|
12
19
|
return 'component' in product && product.component !== undefined;
|
|
13
20
|
}
|
|
14
21
|
|
|
22
|
+
export function isProductAdd(product: ProductMetadata | ProductMetadataSinglePage): product is ProductMetadata {
|
|
23
|
+
return !('component' in product && product.component !== undefined);
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
export function isProductChildGroup(child: ProductChild): child is ProductChildGroup {
|
|
16
|
-
return 'children' in child;
|
|
27
|
+
return child.sideMenu !== undefined && 'children' in child.sideMenu;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
export function isProductChildWithComponent(child: ProductChild): child is ProductChildCustomPage {
|
package/core/plugin-products.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { IExtension } from '@shell/core/types';
|
|
2
|
-
import {
|
|
3
|
-
StandardProductName, ProductChild,
|
|
4
|
-
ProductMetadata, ProductSinglePage,
|
|
5
|
-
} from '@shell/core/plugin-types';
|
|
6
2
|
import { BasePluginProduct } from '@shell/core/plugin-products-base';
|
|
7
3
|
import { TopLevelPluginProduct } from '@shell/core/plugin-products-top-level';
|
|
8
4
|
import { ExtendingPluginProduct } from '@shell/core/plugin-products-extending';
|
|
5
|
+
import { ProductChild, ProductMetadata, ProductMetadataSinglePage, StandardProductName } from '@shell/core/plugin-products-external';
|
|
6
|
+
import { ProductFunction } from '@shell/core/plugin';
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* Factory class for creating plugin products
|
|
@@ -15,13 +13,13 @@ import { ExtendingPluginProduct } from '@shell/core/plugin-products-extending';
|
|
|
15
13
|
export class PluginProduct {
|
|
16
14
|
private instance: BasePluginProduct;
|
|
17
15
|
|
|
18
|
-
constructor(plugin: IExtension, product: StandardProductName | string | ProductMetadata |
|
|
16
|
+
constructor(plugin: IExtension, product: StandardProductName | string | ProductMetadata | ProductMetadataSinglePage | ProductFunction, pages: ProductChild[]) {
|
|
19
17
|
if (typeof product === 'object' && product.name) {
|
|
20
18
|
// This is a new product being added
|
|
21
|
-
this.instance = new TopLevelPluginProduct(plugin, product,
|
|
19
|
+
this.instance = new TopLevelPluginProduct(plugin, product, pages);
|
|
22
20
|
} else if (typeof product === 'string') {
|
|
23
21
|
// This is extending an existing standard product
|
|
24
|
-
this.instance = new ExtendingPluginProduct(plugin, product,
|
|
22
|
+
this.instance = new ExtendingPluginProduct(plugin, product, pages);
|
|
25
23
|
} else {
|
|
26
24
|
// at this point we may not know the product name
|
|
27
25
|
throw new Error('Extensions product registration error ::: Invalid product');
|
package/core/plugin-types.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { PluginRouteRecordRaw } from './types';
|
|
3
|
-
import { NAME as EXPLORER_PROD_NAME } from '@shell/config/product/explorer.js';
|
|
4
|
-
import { NAME as CLUSTER_MAN_PROD_NAME } from '@shell/config/product/manager.js';
|
|
5
|
-
import { NAME as SETTINGS_PROD_NAME } from '@shell/config/product/settings.js';
|
|
6
|
-
import { NAME as AUTH_PROD_NAME } from '@shell/config/product/auth.js';
|
|
7
|
-
import { ProductOptions, HeaderOptions, PaginationHeaderOptions } from '@shell/core/types';
|
|
8
|
-
|
|
9
|
-
type Async<T> = () => Promise<T>;
|
|
1
|
+
import { RouteRecordRaw } from 'vue-router';
|
|
10
2
|
|
|
11
3
|
export type RouteRecordRawWithParams = Omit<RouteRecordRaw, 'redirect' | 'children' | 'path'> & {
|
|
12
4
|
/** Path for route - can include dynamic segments like ':id'. Based on vue-router routes */
|
|
@@ -18,383 +10,3 @@ export type RouteRecordRawWithParams = Omit<RouteRecordRaw, 'redirect' | 'childr
|
|
|
18
10
|
/** Optional redirect */
|
|
19
11
|
redirect?: RouteRecordRaw['redirect'];
|
|
20
12
|
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Product registration route generation options
|
|
24
|
-
*/
|
|
25
|
-
export type ProductRegistrationRouteGenerationOptions = {
|
|
26
|
-
/**
|
|
27
|
-
* Whether to generate the route for an existing product - extending a product - or a new top level product
|
|
28
|
-
*/
|
|
29
|
-
extendProduct? : boolean;
|
|
30
|
-
/**
|
|
31
|
-
* Component to be used for the route (used in virtualType and configureType routes)
|
|
32
|
-
*/
|
|
33
|
-
component?: any;
|
|
34
|
-
/**
|
|
35
|
-
* Generated route should omit the path property
|
|
36
|
-
*/
|
|
37
|
-
omitPath?: boolean;
|
|
38
|
-
/**
|
|
39
|
-
* @internal
|
|
40
|
-
* Whether the route should start with the product name or not (e.g. "my-product/c/:cluster/:resource" vs "c/:cluster/my-product/:resource")
|
|
41
|
-
* only to be used in very special usecases (internal use only - check FLEET product config for an example)
|
|
42
|
-
* */
|
|
43
|
-
startRouteWithProduct?: boolean;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Represents the allowed extensible products in Rancher Dashboard
|
|
48
|
-
*/
|
|
49
|
-
export const StandardProductNames = {
|
|
50
|
-
EXPLORER: EXPLORER_PROD_NAME,
|
|
51
|
-
MANAGER: CLUSTER_MAN_PROD_NAME,
|
|
52
|
-
SETTINGS: SETTINGS_PROD_NAME,
|
|
53
|
-
AUTH: AUTH_PROD_NAME,
|
|
54
|
-
} as const;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Type representing a standard product name value
|
|
58
|
-
*/
|
|
59
|
-
export type StandardProductName = (typeof StandardProductNames)[keyof typeof StandardProductNames];
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Represents allowed configuration for a child page
|
|
63
|
-
*/
|
|
64
|
-
export type ProductChildMetadata = {
|
|
65
|
-
/** Product name/unique identifier for the product */
|
|
66
|
-
name: string;
|
|
67
|
-
/** Ordering weight for the among its siblings, if applicable */
|
|
68
|
-
weight?: number;
|
|
69
|
-
} & (
|
|
70
|
-
/** Human-readable label for the child page
|
|
71
|
-
* Either label or labelKey are required */
|
|
72
|
-
{ label: string; labelKey?: string }
|
|
73
|
-
/** Translation key for the label of the child page
|
|
74
|
-
* Either label or labelKey are required */
|
|
75
|
-
| { labelKey: string; label?: string }
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Represents the allowed configuration for a custom page (virtualType)
|
|
80
|
-
*/
|
|
81
|
-
export type CustomPageConfiguration = {
|
|
82
|
-
/** Display only if condition is met (relates to IF_HAVE in shell/store/type-map) */
|
|
83
|
-
ifHave?: boolean;
|
|
84
|
-
/** Display only if feature is present (relates to shell/store/features) */
|
|
85
|
-
ifFeature?: string;
|
|
86
|
-
/** Display only if resource type exists */
|
|
87
|
-
ifHaveType?: string;
|
|
88
|
-
/** Used in conjunction with "ifHaveType", display only if resource type allows this verb (GET, POST, PUT, DELETE) */
|
|
89
|
-
ifHaveVerb?: string;
|
|
90
|
-
/**
|
|
91
|
-
* @internal
|
|
92
|
-
* Display label for the custom page
|
|
93
|
-
*/
|
|
94
|
-
label?: string;
|
|
95
|
-
/**
|
|
96
|
-
* @internal
|
|
97
|
-
* Translation key for the label
|
|
98
|
-
* */
|
|
99
|
-
labelKey?: string;
|
|
100
|
-
/**
|
|
101
|
-
* @internal
|
|
102
|
-
* Name of the page (unique identifier)
|
|
103
|
-
*/
|
|
104
|
-
name?: string;
|
|
105
|
-
/**
|
|
106
|
-
* @internal
|
|
107
|
-
* Entry route definition for this custom page
|
|
108
|
-
*/
|
|
109
|
-
route?: RouteRecordRawWithParams | PluginRouteRecordRaw | Object;
|
|
110
|
-
/**
|
|
111
|
-
* @internal
|
|
112
|
-
* Icon for the custom page (relates to icons in https://github.com/rancher/icons)
|
|
113
|
-
*/
|
|
114
|
-
icon?: 'compass';
|
|
115
|
-
/** Whether this custom page is namespaced or not */
|
|
116
|
-
namespaced?: boolean;
|
|
117
|
-
/**
|
|
118
|
-
* @internal
|
|
119
|
-
* Ordering weight for the custom page
|
|
120
|
-
*/
|
|
121
|
-
weight?: number;
|
|
122
|
-
/** Whether this custom page is exact match */
|
|
123
|
-
exact?: boolean;
|
|
124
|
-
/**
|
|
125
|
-
* @internal
|
|
126
|
-
* Whether this custom page will act as an overview page
|
|
127
|
-
* */
|
|
128
|
-
overview?: boolean;
|
|
129
|
-
/** Whether this custom page has an exact path match */
|
|
130
|
-
'exact-path'?: boolean;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Represents the allowed configuration for a resource page (configureType)
|
|
135
|
-
*/
|
|
136
|
-
export type ResourcePageConfiguration = {
|
|
137
|
-
/**
|
|
138
|
-
* @internal
|
|
139
|
-
* Entry route definition for this custom page
|
|
140
|
-
*/
|
|
141
|
-
/** Override for the name displayed */
|
|
142
|
-
displayName?: string;
|
|
143
|
-
/** Override for the create button string on a list view */
|
|
144
|
-
listCreateButtonLabelKey?: string;
|
|
145
|
-
/** If false, disable create even if schema says it's allowed */
|
|
146
|
-
isCreatable?: boolean;
|
|
147
|
-
/** If false, disable for edit */
|
|
148
|
-
isEditable?: boolean;
|
|
149
|
-
/** If false, disable for remove/delete */
|
|
150
|
-
isRemovable?: boolean;
|
|
151
|
-
/** If false, hide state in columns and masthead */
|
|
152
|
-
showState?: boolean;
|
|
153
|
-
/** If false, hide age in columns and masthead */
|
|
154
|
-
showAge?: boolean;
|
|
155
|
-
/** If false, hide masthead config button in view mode */
|
|
156
|
-
showConfigView?: boolean;
|
|
157
|
-
/** If false, hide masthead in list view */
|
|
158
|
-
showListMasthead?: boolean;
|
|
159
|
-
/** If false, cannot edit or show yaml */
|
|
160
|
-
canYaml?: boolean;
|
|
161
|
-
/** Show the Masthead in the edit resource component */
|
|
162
|
-
resourceEditMasthead?: boolean;
|
|
163
|
-
/**
|
|
164
|
-
* @internal
|
|
165
|
-
* Entry route definition for this resource page
|
|
166
|
-
*/
|
|
167
|
-
customRoute?: RouteRecordRawWithParams;
|
|
168
|
-
/** Hide this type from the nav/search bar on downstream clusters (will only show in "local" cluster) */
|
|
169
|
-
localOnly?: boolean;
|
|
170
|
-
/** Whether this custom page is namespaced or not */
|
|
171
|
-
namespaced?: boolean;
|
|
172
|
-
/**
|
|
173
|
-
* @internal
|
|
174
|
-
* Whether this custom page has list groups (definition for grouping items in the list view)
|
|
175
|
-
*/
|
|
176
|
-
listGroups?: {
|
|
177
|
-
/** Icon for the group (relates to icons in rancher-icons */
|
|
178
|
-
icon?: string;
|
|
179
|
-
/** Value for the group (used for grouping items in the list view) */
|
|
180
|
-
value?: string;
|
|
181
|
-
/** Field for the group (used for grouping items in the list view) */
|
|
182
|
-
field?: string;
|
|
183
|
-
/** Column to hide when this group is active */
|
|
184
|
-
hideColumn?: string;
|
|
185
|
-
/** Tooltip key for the group */
|
|
186
|
-
tooltipKey?: string;
|
|
187
|
-
}[];
|
|
188
|
-
/**
|
|
189
|
-
* @internal
|
|
190
|
-
* Whether the provided list groups will override the default grouping options (e.g. group by namespace, group by cluster, etc.) or be added to them
|
|
191
|
-
*/
|
|
192
|
-
listGroupsWillOverride?: boolean;
|
|
193
|
-
/**
|
|
194
|
-
* @internal
|
|
195
|
-
* Use this to configure subtypes that should be shown in the list view for this type (e.g. show "pods" and "deployments" in the list view for "workloads")
|
|
196
|
-
*/
|
|
197
|
-
subTypes?: string[];
|
|
198
|
-
// resource: undefined; // Use this resource in ResourceDetails instead
|
|
199
|
-
// resourceDetail: undefined; // Use this resource specifically for ResourceDetail's detail component
|
|
200
|
-
// resourceEdit: undefined; // Use this resource specifically for ResourceDetail's edit component
|
|
201
|
-
// depaginate: undefined; // Use this to depaginate requests for this type
|
|
202
|
-
// notFilterNamespace: undefined; // Define namespaces that do not need to be filtered
|
|
203
|
-
// used in configureType options, to be typed later if needed
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Represents a Vue component or an async function that resolves to a Vue component, used for route components in product configuration
|
|
208
|
-
*/
|
|
209
|
-
export type VueRouteComponent = RouteComponent | Async<RouteComponent>;
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Metadata for route generation to a product overview page
|
|
213
|
-
*/
|
|
214
|
-
export type OverviewPageRoutingMetadata = {
|
|
215
|
-
/** Name of the overview page */
|
|
216
|
-
name: string;
|
|
217
|
-
/** Component to render for the overview page */
|
|
218
|
-
component: VueRouteComponent;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Represents a resource page with a type (K8s resource)
|
|
223
|
-
*/
|
|
224
|
-
export type ProductChildResourcePage = {
|
|
225
|
-
/** K8s resource type name for a resource page */
|
|
226
|
-
type: string;
|
|
227
|
-
/** Optional configuration for the resource page */
|
|
228
|
-
config?: ResourcePageConfiguration;
|
|
229
|
-
/** Ordering weight for this page among its siblings */
|
|
230
|
-
weight?: number;
|
|
231
|
-
/** Use this to override the resource name used in the list view for this type */
|
|
232
|
-
overrideListResourceName?: string;
|
|
233
|
-
/** Whether to hide this resource from the side-menu entirely */
|
|
234
|
-
hideFromNav?: boolean;
|
|
235
|
-
/** Whether to hide bulk actions for this resource */
|
|
236
|
-
hideBulkActions?: boolean;
|
|
237
|
-
/** Table headers for this resource type (client-side pagination) */
|
|
238
|
-
headers?: HeaderOptions[];
|
|
239
|
-
/** Table headers for this resource type (server-side pagination) */
|
|
240
|
-
sspHeaders?: PaginationHeaderOptions[];
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Represents a custom page with a component
|
|
245
|
-
*/
|
|
246
|
-
export type ProductChildCustomPage = ProductChildMetadata & {
|
|
247
|
-
/** Component to render for this custom page */
|
|
248
|
-
component: VueRouteComponent;
|
|
249
|
-
/** Optional configuration for the page */
|
|
250
|
-
config?: CustomPageConfiguration;
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Represents a page item (custom page or resource page) in a product's config
|
|
255
|
-
* - For custom pages: use `component` with `name` and `label`/`labelKey`
|
|
256
|
-
* - For resource pages: use `type` with optional `config` and `headers`
|
|
257
|
-
*/
|
|
258
|
-
export type ProductChildPage = ProductChildCustomPage | ProductChildResourcePage;
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Represents a product child in the navigation
|
|
262
|
-
* This can be a type, a group, or a route.
|
|
263
|
-
*/
|
|
264
|
-
export type ProductChild = ProductChildGroup | ProductChildPage; // eslint-disable-line no-use-before-define
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Represents a group of child pages in a product configuration
|
|
268
|
-
*/
|
|
269
|
-
export type ProductChildGroup = ProductChildMetadata & {
|
|
270
|
-
component?: VueRouteComponent;
|
|
271
|
-
children: ProductChild[];
|
|
272
|
-
/** Default child to navigate to */
|
|
273
|
-
default?: string;
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Represents the allowed configuration for a product
|
|
278
|
-
*/
|
|
279
|
-
export type ProductMetadata = Omit<ProductOptions, 'name' | 'label' | 'labelKey' | 'category' | 'to' | 'version' | 'inStore'> & {
|
|
280
|
-
/**
|
|
281
|
-
* Product name (unique identifier)
|
|
282
|
-
*/
|
|
283
|
-
name: string;
|
|
284
|
-
/**
|
|
285
|
-
* @internal
|
|
286
|
-
* Use `renameGroups` on the product metadata to remap group display names in the side-menu. Each entry matches a group's internal ID (via string or regex) and replaces its display label with a new name. This only changes how the group is labelled in the UI — it does not move resources between groups.
|
|
287
|
-
*
|
|
288
|
-
* The `groupSelector` is evaluated against group internal IDs. It can be an exact string or a `RegExp` pattern. The `newName` value is the new display name.
|
|
289
|
-
*
|
|
290
|
-
* const product: ProductMetadata = {
|
|
291
|
-
* name: 'my-app',
|
|
292
|
-
* label: 'My App',
|
|
293
|
-
* renameGroups: [
|
|
294
|
-
* // Rename a group with an ugly internal ID to a friendlier display name
|
|
295
|
-
* { groupSelector: 'cert-manager.io', newName: 'Certificates' },
|
|
296
|
-
* // Use a regex to rename all groups matching a pattern
|
|
297
|
-
* { groupSelector: /^networking\./, newName: 'Networking' },
|
|
298
|
-
* ],
|
|
299
|
-
* };
|
|
300
|
-
*/
|
|
301
|
-
renameGroups?: {
|
|
302
|
-
/** String or regex to match against group internal IDs */
|
|
303
|
-
groupSelector: RegExp | string;
|
|
304
|
-
/** Display name to use for matching groups */
|
|
305
|
-
newName: string;
|
|
306
|
-
}[];
|
|
307
|
-
/**
|
|
308
|
-
* @internal
|
|
309
|
-
*
|
|
310
|
-
* Use `moveToGroup` on the product metadata to move pages (resource types or custom pages) into specific side-menu groups. This is useful when a page should appear inside a group but isn't defined as a child of that group in the config.
|
|
311
|
-
* Each entry identifies a page by its `entryId` — the resource `type` string or the custom page `name` — and moves it into the specified group. Use the group's `name` as you defined it in your config.
|
|
312
|
-
*
|
|
313
|
-
* const monitoringGroup: ProductChildGroup = {
|
|
314
|
-
* name: 'monitoring',
|
|
315
|
-
* label: 'Monitoring',
|
|
316
|
-
* children: [
|
|
317
|
-
* { name: 'alerts', label: 'Alerts', component: () => import('./pages/Alerts.vue') },
|
|
318
|
-
* ],
|
|
319
|
-
* };
|
|
320
|
-
|
|
321
|
-
* const dashboardPage: ProductChildCustomPage = {
|
|
322
|
-
* name: 'dashboard', label: 'Dashboard', component: () => import('./pages/Dashboard.vue'),
|
|
323
|
-
* };
|
|
324
|
-
|
|
325
|
-
* const product: ProductMetadata = {
|
|
326
|
-
* name: 'my-app',
|
|
327
|
-
* label: 'My App',
|
|
328
|
-
* moveToGroup: [
|
|
329
|
-
* // Move the 'pod' resource type into the 'monitoring' group
|
|
330
|
-
* { entryId: 'pod', groupName: 'monitoring' },
|
|
331
|
-
* // Move a custom page into the 'monitoring' group
|
|
332
|
-
* { entryId: 'dashboard', groupName: 'monitoring' },
|
|
333
|
-
* ],
|
|
334
|
-
* };
|
|
335
|
-
*
|
|
336
|
-
* extension.addProduct(product, [monitoringGroup, { type: 'pod' }, dashboardPage]);
|
|
337
|
-
*
|
|
338
|
-
* Note: The `entryId` must match a page declared in the same product config — either a resource page's `type` or a custom page's `name`. The target `groupName` must be a `ProductChildGroup` defined in the same config. If either is not found, an error is thrown at registration time listing the available options. Only exact string identifiers are supported (no regex).
|
|
339
|
-
*
|
|
340
|
-
* The optional `weight` parameter controls precedence when multiple `moveToGroup` rules target the same page (default: `5`). Higher weight takes precedence.
|
|
341
|
-
*/
|
|
342
|
-
moveToGroup?: {
|
|
343
|
-
/** Page identifier — the resource `type` string or the custom page `name` */
|
|
344
|
-
entryId: string;
|
|
345
|
-
/** Target group name as defined in your group config (`name` property) */
|
|
346
|
-
groupName: string;
|
|
347
|
-
/** Ordering weight for the mapping (default: 5). Higher weight takes precedence when multiple rules match */
|
|
348
|
-
weight?: number;
|
|
349
|
-
}[];
|
|
350
|
-
/**
|
|
351
|
-
* @internal
|
|
352
|
-
*
|
|
353
|
-
* maps to DSL ignoreGroup
|
|
354
|
-
*
|
|
355
|
-
* Use `ignoreGroups` on the product metadata to hide specific side-menu groups. Each entry specifies a `groupSelector` to match group names — either an exact string or a regex pattern.
|
|
356
|
-
*
|
|
357
|
-
* The `condition` callback is optional. When provided, it receives the store getters and returns `true` to hide the group (conditional hide). When omitted, the group is always hidden (unconditional hide).
|
|
358
|
-
*
|
|
359
|
-
* Example usage:
|
|
360
|
-
* const product: ProductMetadata = {
|
|
361
|
-
* name: 'my-app',
|
|
362
|
-
* label: 'My App',
|
|
363
|
-
* ignoreGroups: [
|
|
364
|
-
* // Always hide the "internal" group (unconditional — no condition)
|
|
365
|
-
* { groupSelector: 'internal' },
|
|
366
|
-
* // Hide all groups matching a regex pattern (unconditional)
|
|
367
|
-
* { groupSelector: /^deprecated/ },
|
|
368
|
-
* // Conditionally hide based on a feature flag
|
|
369
|
-
* {
|
|
370
|
-
* groupSelector: 'experimental',
|
|
371
|
-
* condition: (getters) => !getters['features/isEnabled']('experimental-feature'),
|
|
372
|
-
* },
|
|
373
|
-
* ],
|
|
374
|
-
* };
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* In this example, the "internal" group is always hidden, any group with a name starting with "deprecated-" is hidden, and the "experimental" group is hidden unless the "experimental-feature" flag is enabled in the store.
|
|
378
|
-
*/
|
|
379
|
-
ignoreGroups?: {
|
|
380
|
-
/** String or regex to match against group names */
|
|
381
|
-
groupSelector: string | RegExp;
|
|
382
|
-
/** Optional conditional function that accepts the root Dashboard Vuex store getters and returns true if the group should be ignored */
|
|
383
|
-
condition?: (getters: any) => boolean }[];
|
|
384
|
-
} & (
|
|
385
|
-
/** Human-readable label for the product
|
|
386
|
-
* Either label or labelKey are required */
|
|
387
|
-
| { label: string; labelKey?: string }
|
|
388
|
-
/** Translation key for the label of the product
|
|
389
|
-
* Either label or labelKey are required */
|
|
390
|
-
| { labelKey: string; label?: string }
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Represents a single page product, which is a product that only has one page and
|
|
395
|
-
* therefore does not need to be represented in the navigation as a group with children.
|
|
396
|
-
*/
|
|
397
|
-
export type ProductSinglePage = ProductMetadata & {
|
|
398
|
-
/** Component to render for this product (single page product) */
|
|
399
|
-
component: VueRouteComponent;
|
|
400
|
-
};
|
package/core/plugin.ts
CHANGED
|
@@ -21,18 +21,17 @@ import {
|
|
|
21
21
|
ServerSidePaginationExtensionConfig,
|
|
22
22
|
TableAction,
|
|
23
23
|
} from './types';
|
|
24
|
-
import {
|
|
25
|
-
ProductMetadata,
|
|
26
|
-
ProductSinglePage,
|
|
27
|
-
ProductChild,
|
|
28
|
-
StandardProductName,
|
|
29
|
-
RouteRecordRawWithParams
|
|
30
|
-
} from './plugin-types';
|
|
24
|
+
import { RouteRecordRawWithParams } from './plugin-types';
|
|
31
25
|
import coreStore, { coreStoreModule, coreStoreState } from '@shell/plugins/dashboard-store';
|
|
32
26
|
import { defineAsyncComponent, markRaw, Component } from 'vue';
|
|
33
27
|
import { getVersionData, CURRENT_RANCHER_VERSION } from '@shell/config/version';
|
|
34
28
|
import { ExtensionManagerTypes } from '@shell/types/extension-manager';
|
|
35
29
|
import { PluginProduct } from './plugin-products';
|
|
30
|
+
import {
|
|
31
|
+
ProductMetadata, ProductMetadataSinglePage,
|
|
32
|
+
StandardProductName,
|
|
33
|
+
ProductChild
|
|
34
|
+
} from '@shell/core/plugin-products-external';
|
|
36
35
|
|
|
37
36
|
/** Registration IDs used for different extension points in the extensions catalog */
|
|
38
37
|
export const EXT_IDS = {
|
|
@@ -137,16 +136,16 @@ export class Plugin implements IPlugin {
|
|
|
137
136
|
return storeDSL;
|
|
138
137
|
}
|
|
139
138
|
|
|
140
|
-
addProduct(product: ProductFunction | ProductMetadata |
|
|
139
|
+
addProduct(product: ProductFunction | ProductMetadata | ProductMetadataSinglePage | string, pages?: ProductChild[]): void {
|
|
141
140
|
let pluginProduct: PluginProduct;
|
|
142
141
|
|
|
143
142
|
if (typeof product === 'string') {
|
|
144
143
|
pluginProduct = PluginProduct.fromName(this, product);
|
|
145
144
|
} else if (product?.name) {
|
|
146
|
-
if (!
|
|
147
|
-
pluginProduct = new PluginProduct(this, product
|
|
145
|
+
if (!pages) {
|
|
146
|
+
pluginProduct = new PluginProduct(this, product, []);
|
|
148
147
|
} else {
|
|
149
|
-
pluginProduct = new PluginProduct(this, product
|
|
148
|
+
pluginProduct = new PluginProduct(this, product, pages);
|
|
150
149
|
}
|
|
151
150
|
} else {
|
|
152
151
|
this.products.push(product as ProductFunction);
|