@rancher/shell 3.0.8-rc.9 → 3.0.9-rc.1
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/impl/apis.ts +61 -0
- package/apis/index.ts +40 -0
- package/apis/intf/modal.ts +128 -0
- package/apis/intf/shell.ts +36 -0
- package/apis/intf/slide-in.ts +100 -0
- package/apis/intf/system.ts +41 -0
- package/apis/shell/__tests__/modal.test.ts +80 -0
- package/apis/shell/__tests__/notifications.test.ts +71 -0
- package/apis/shell/__tests__/slide-in.test.ts +90 -0
- package/apis/shell/__tests__/system.test.ts +129 -0
- package/apis/shell/index.ts +38 -0
- package/apis/shell/modal.ts +41 -0
- package/apis/shell/notifications.ts +65 -0
- package/apis/shell/slide-in.ts +37 -0
- package/apis/shell/system.ts +65 -0
- package/apis/vue-shim.d.ts +11 -0
- package/assets/styles/global/_tooltip.scss +6 -1
- package/assets/translations/en-us.yaml +5 -0
- package/components/ActionMenuShell.vue +3 -1
- package/components/CruResource.vue +8 -1
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +50 -1
- package/components/Drawer/ResourceDetailDrawer/composables.ts +19 -0
- package/components/Drawer/ResourceDetailDrawer/index.vue +3 -1
- package/components/LocaleSelector.vue +2 -2
- package/components/ModalManager.vue +11 -1
- package/components/Questions/__tests__/Yaml.test.ts +1 -1
- package/components/RelatedResources.vue +5 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +5 -1
- package/components/ResourceDetail/Masthead/latest.vue +23 -21
- package/components/ResourceDetail/index.vue +3 -0
- package/components/ResourceTable.vue +54 -21
- package/components/SlideInPanelManager.vue +16 -11
- package/components/SortableTable/THead.vue +2 -1
- package/components/SortableTable/index.vue +20 -2
- package/components/Tabbed/index.vue +37 -2
- package/components/__tests__/NamespaceFilter.test.ts +49 -0
- package/components/auth/SelectPrincipal.vue +4 -0
- package/components/auth/login/ldap.vue +3 -3
- package/components/fleet/FleetSecretSelector.vue +1 -1
- package/components/form/KeyValue.vue +1 -1
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/NodeScheduling.vue +2 -2
- package/components/form/ResourceTabs/composable.ts +2 -2
- package/components/form/ResourceTabs/index.vue +0 -2
- package/components/form/__tests__/NameNsDescription.test.ts +42 -0
- package/components/formatter/LinkName.vue +5 -0
- package/components/nav/Group.vue +25 -7
- package/components/nav/Header.vue +1 -1
- package/components/nav/NamespaceFilter.vue +1 -0
- package/components/nav/Type.vue +17 -6
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +1 -1
- package/components/nav/__tests__/Type.test.ts +59 -0
- package/composables/cruResource.ts +27 -0
- package/composables/focusTrap.ts +3 -1
- package/composables/resourceDetail.ts +15 -0
- package/composables/useLabeledFormElement.ts +3 -4
- package/config/product/fleet.js +1 -1
- package/config/router/navigation-guards/clusters.js +3 -3
- package/config/router/navigation-guards/products.js +1 -1
- package/config/router/routes.js +1 -5
- package/core/__tests__/extension-manager-impl.test.js +437 -0
- package/core/extension-manager-impl.js +6 -27
- package/core/plugin-helpers.ts +2 -2
- package/core/plugin.ts +9 -1
- package/core/plugins-loader.js +2 -2
- package/core/types-provisioning.ts +4 -0
- package/core/types.ts +35 -0
- package/detail/catalog.cattle.io.app.vue +1 -0
- package/detail/provisioning.cattle.io.cluster.vue +8 -6
- package/dialog/DeveloperLoadExtensionDialog.vue +1 -1
- package/dialog/MoveNamespaceDialog.vue +20 -4
- package/dialog/SearchDialog.vue +1 -0
- package/dialog/__tests__/MoveNamespaceDialog.test.ts +249 -0
- package/directives/__tests__/clean-tooltip.test.ts +298 -0
- package/directives/clean-tooltip.ts +234 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -2
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +98 -1
- package/edit/fleet.cattle.io.helmop.vue +5 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
- package/edit/provisioning.cattle.io.cluster/index.vue +5 -5
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -8
- package/edit/resources.cattle.io.restore.vue +1 -1
- package/edit/workload/Job.vue +2 -2
- package/edit/workload/__tests__/index.test.ts +123 -85
- package/edit/workload/index.vue +2 -2
- package/edit/workload/mixins/workload.js +19 -1
- package/initialize/install-plugins.js +4 -5
- package/machine-config/azure.vue +1 -1
- package/machine-config/components/GCEImage.vue +1 -1
- package/mixins/__tests__/brand.spec.ts +18 -13
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +16 -0
- package/models/chart.js +70 -74
- package/models/management.cattle.io.cluster.js +1 -1
- package/models/provisioning.cattle.io.cluster.js +11 -3
- package/package.json +7 -7
- package/pages/auth/login.vue +3 -3
- package/pages/auth/setup.vue +1 -1
- package/pages/auth/verify.vue +3 -3
- package/pages/c/_cluster/apps/charts/index.vue +122 -24
- package/pages/c/_cluster/apps/charts/install.vue +33 -0
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
- package/pages/c/_cluster/fleet/index.vue +7 -10
- package/pages/c/_cluster/settings/index.vue +5 -0
- package/pkg/auto-import.js +3 -3
- package/pkg/dynamic-importer.lib.js +1 -1
- package/pkg/import.js +1 -1
- package/plugins/__tests__/mutations.tests.ts +179 -0
- package/plugins/dashboard-store/getters.js +1 -1
- package/plugins/dashboard-store/model-loader.js +1 -1
- package/plugins/dashboard-store/mutations.js +23 -2
- package/plugins/dashboard-store/resource-class.js +8 -3
- package/plugins/plugin.js +2 -2
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +301 -128
- package/plugins/steve/mutations.js +9 -0
- package/plugins/steve/steve-class.js +1 -1
- package/plugins/steve/steve-pagination-utils.ts +108 -43
- package/plugins/steve/subscribe.js +23 -2
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
- package/rancher-components/RcDropdown/useDropdownContext.ts +2 -4
- package/rancher-components/RcItemCard/RcItemCard.vue +1 -1
- package/scripts/publish-shell.sh +25 -0
- package/store/__tests__/catalog.test.ts +1 -1
- package/store/__tests__/type-map.test.ts +164 -2
- package/store/auth.js +23 -11
- package/store/i18n.js +3 -3
- package/store/index.js +5 -3
- package/store/notifications.ts +2 -0
- package/store/prefs.js +2 -2
- package/store/type-map.js +17 -7
- package/types/internal-api/shell/modal.d.ts +6 -6
- package/types/notifications/index.ts +126 -15
- package/types/rancher/index.d.ts +9 -0
- package/types/shell/index.d.ts +16 -1
- package/types/store/dashboard-store.types.ts +29 -7
- package/types/vue-shim.d.ts +5 -4
- package/utils/__tests__/router.test.js +238 -0
- package/utils/cluster.js +4 -1
- package/utils/cspAdaptor.ts +32 -14
- package/utils/fleet.ts +8 -1
- package/utils/pagination-utils.ts +2 -2
- package/utils/pagination-wrapper.ts +4 -4
- package/utils/router.js +50 -0
- package/utils/unit-tests/pagination-utils.spec.ts +8 -8
- package/vue.config.js +3 -3
- package/composables/useExtensionManager.ts +0 -17
- package/core/__test__/extension-manager-impl.test.js +0 -236
- package/core/plugins.js +0 -38
- package/directives/clean-tooltip.js +0 -32
- package/plugins/internal-api/index.ts +0 -37
- package/plugins/internal-api/shared/base-api.ts +0 -13
- package/plugins/internal-api/shell/shell.api.ts +0 -108
- package/types/internal-api/shell/growl.d.ts +0 -25
- package/types/internal-api/shell/slideIn.d.ts +0 -15
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { findRouteDefinitionByName, filterLocationValidParams } from '@shell/utils/router';
|
|
2
|
+
|
|
3
|
+
describe('findRouteDefinitionByName', () => {
|
|
4
|
+
const createMockRouter = (routes) => ({ getRoutes: () => routes });
|
|
5
|
+
|
|
6
|
+
it('should find a route by its name', () => {
|
|
7
|
+
const routes = [
|
|
8
|
+
{ name: 'home', path: '/' },
|
|
9
|
+
{ name: 'about', path: '/about' },
|
|
10
|
+
{ name: 'c-cluster', path: '/c/:cluster' },
|
|
11
|
+
];
|
|
12
|
+
const router = createMockRouter(routes);
|
|
13
|
+
|
|
14
|
+
const result = findRouteDefinitionByName(router, 'about');
|
|
15
|
+
|
|
16
|
+
expect(result).toStrictEqual({ name: 'about', path: '/about' });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should return the first matching route when name exists', () => {
|
|
20
|
+
const routes = [
|
|
21
|
+
{ name: 'c-cluster-explorer', path: '/c/:cluster/explorer' },
|
|
22
|
+
{ name: 'c-cluster-apps', path: '/c/:cluster/apps' },
|
|
23
|
+
];
|
|
24
|
+
const router = createMockRouter(routes);
|
|
25
|
+
|
|
26
|
+
const result = findRouteDefinitionByName(router, 'c-cluster-explorer');
|
|
27
|
+
|
|
28
|
+
expect(result).toStrictEqual({ name: 'c-cluster-explorer', path: '/c/:cluster/explorer' });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should return undefined when route name is not found', () => {
|
|
32
|
+
const routes = [
|
|
33
|
+
{ name: 'home', path: '/' },
|
|
34
|
+
{ name: 'about', path: '/about' },
|
|
35
|
+
];
|
|
36
|
+
const router = createMockRouter(routes);
|
|
37
|
+
|
|
38
|
+
const result = findRouteDefinitionByName(router, 'nonexistent');
|
|
39
|
+
|
|
40
|
+
expect(result).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return undefined when routes array is empty', () => {
|
|
44
|
+
const router = createMockRouter([]);
|
|
45
|
+
|
|
46
|
+
const result = findRouteDefinitionByName(router, 'any-route');
|
|
47
|
+
|
|
48
|
+
expect(result).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should handle routes with additional properties', () => {
|
|
52
|
+
const routes = [
|
|
53
|
+
{
|
|
54
|
+
name: 'c-cluster-product-resource',
|
|
55
|
+
path: '/c/:cluster/:product/:resource',
|
|
56
|
+
meta: { requiresAuthentication: true },
|
|
57
|
+
props: true,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
const router = createMockRouter(routes);
|
|
61
|
+
|
|
62
|
+
const result = findRouteDefinitionByName(router, 'c-cluster-product-resource');
|
|
63
|
+
|
|
64
|
+
expect(result).toStrictEqual(routes[0]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should match exact route names only', () => {
|
|
68
|
+
const routes = [
|
|
69
|
+
{ name: 'c-cluster', path: '/c/:cluster' },
|
|
70
|
+
{ name: 'c-cluster-explorer', path: '/c/:cluster/explorer' },
|
|
71
|
+
];
|
|
72
|
+
const router = createMockRouter(routes);
|
|
73
|
+
|
|
74
|
+
const result = findRouteDefinitionByName(router, 'c-cluster');
|
|
75
|
+
|
|
76
|
+
expect(result).toStrictEqual({ name: 'c-cluster', path: '/c/:cluster' });
|
|
77
|
+
expect(result.name).not.toBe('c-cluster-explorer');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('filterLocationValidParams', () => {
|
|
82
|
+
const createMockRouter = (routes) => ({ getRoutes: () => routes });
|
|
83
|
+
|
|
84
|
+
it('should filter out params not in route path', () => {
|
|
85
|
+
const routes = [
|
|
86
|
+
{ name: 'c-cluster', path: '/c/:cluster' },
|
|
87
|
+
];
|
|
88
|
+
const router = createMockRouter(routes);
|
|
89
|
+
const routeRecord = {
|
|
90
|
+
name: 'c-cluster',
|
|
91
|
+
params: {
|
|
92
|
+
cluster: 'local',
|
|
93
|
+
product: 'explorer',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
98
|
+
|
|
99
|
+
expect(result.params).toStrictEqual({ cluster: 'local' });
|
|
100
|
+
expect(result.params.product).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should keep all params when all are valid', () => {
|
|
104
|
+
const routes = [
|
|
105
|
+
{ name: 'c-cluster-product-resource', path: '/c/:cluster/:product/:resource' },
|
|
106
|
+
];
|
|
107
|
+
const router = createMockRouter(routes);
|
|
108
|
+
const routeRecord = {
|
|
109
|
+
name: 'c-cluster-product-resource',
|
|
110
|
+
params: {
|
|
111
|
+
cluster: 'local',
|
|
112
|
+
product: 'explorer',
|
|
113
|
+
resource: 'pods',
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
118
|
+
|
|
119
|
+
expect(result.params).toStrictEqual({
|
|
120
|
+
cluster: 'local',
|
|
121
|
+
product: 'explorer',
|
|
122
|
+
resource: 'pods',
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should preserve other properties on routeRecord', () => {
|
|
127
|
+
const routes = [
|
|
128
|
+
{ name: 'c-cluster', path: '/c/:cluster' },
|
|
129
|
+
];
|
|
130
|
+
const router = createMockRouter(routes);
|
|
131
|
+
const routeRecord = {
|
|
132
|
+
name: 'c-cluster',
|
|
133
|
+
params: { cluster: 'local' },
|
|
134
|
+
query: { mode: 'edit' },
|
|
135
|
+
hash: '#section',
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
139
|
+
|
|
140
|
+
expect(result.query).toStrictEqual({ mode: 'edit' });
|
|
141
|
+
expect(result.hash).toBe('#section');
|
|
142
|
+
expect(result.name).toBe('c-cluster');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should return routeRecord unchanged when routeRecord is null', () => {
|
|
146
|
+
const router = createMockRouter([]);
|
|
147
|
+
|
|
148
|
+
const result = filterLocationValidParams(router, null);
|
|
149
|
+
|
|
150
|
+
expect(result).toBeNull();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should return routeRecord unchanged when routeRecord is undefined', () => {
|
|
154
|
+
const router = createMockRouter([]);
|
|
155
|
+
|
|
156
|
+
const result = filterLocationValidParams(router, undefined);
|
|
157
|
+
|
|
158
|
+
expect(result).toBeUndefined();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should return routeRecord unchanged when name is missing', () => {
|
|
162
|
+
const router = createMockRouter([]);
|
|
163
|
+
const routeRecord = { params: { cluster: 'local' } };
|
|
164
|
+
|
|
165
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
166
|
+
|
|
167
|
+
expect(result).toStrictEqual(routeRecord);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should return routeRecord unchanged when params is missing', () => {
|
|
171
|
+
const router = createMockRouter([]);
|
|
172
|
+
const routeRecord = { name: 'c-cluster' };
|
|
173
|
+
|
|
174
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
175
|
+
|
|
176
|
+
expect(result).toStrictEqual(routeRecord);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should return routeRecord unchanged when route definition is not found', () => {
|
|
180
|
+
const routes = [
|
|
181
|
+
{ name: 'home', path: '/' },
|
|
182
|
+
];
|
|
183
|
+
const router = createMockRouter(routes);
|
|
184
|
+
const routeRecord = {
|
|
185
|
+
name: 'nonexistent-route',
|
|
186
|
+
params: { cluster: 'local' },
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
190
|
+
|
|
191
|
+
expect(result).toStrictEqual(routeRecord);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should return empty params when no params are valid', () => {
|
|
195
|
+
const routes = [
|
|
196
|
+
{ name: 'home', path: '/' },
|
|
197
|
+
];
|
|
198
|
+
const router = createMockRouter(routes);
|
|
199
|
+
const routeRecord = {
|
|
200
|
+
name: 'home',
|
|
201
|
+
params: {
|
|
202
|
+
cluster: 'local',
|
|
203
|
+
product: 'explorer',
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
208
|
+
|
|
209
|
+
expect(result.params).toStrictEqual({});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should handle optional params in path', () => {
|
|
213
|
+
const routes = [
|
|
214
|
+
{ name: 'c-cluster-product-resource-id', path: '/c/:cluster/:product/:resource/:id?' },
|
|
215
|
+
];
|
|
216
|
+
const router = createMockRouter(routes);
|
|
217
|
+
const routeRecord = {
|
|
218
|
+
name: 'c-cluster-product-resource-id',
|
|
219
|
+
params: {
|
|
220
|
+
cluster: 'local',
|
|
221
|
+
product: 'explorer',
|
|
222
|
+
resource: 'pods',
|
|
223
|
+
id: 'my-pod',
|
|
224
|
+
extra: 'should-be-removed',
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const result = filterLocationValidParams(router, routeRecord);
|
|
229
|
+
|
|
230
|
+
expect(result.params).toStrictEqual({
|
|
231
|
+
cluster: 'local',
|
|
232
|
+
product: 'explorer',
|
|
233
|
+
resource: 'pods',
|
|
234
|
+
id: 'my-pod',
|
|
235
|
+
});
|
|
236
|
+
expect(result.params.extra).toBeUndefined();
|
|
237
|
+
});
|
|
238
|
+
});
|
package/utils/cluster.js
CHANGED
|
@@ -242,7 +242,7 @@ export function filterOutDeprecatedPatchVersions(allVersions, currentVersion) {
|
|
|
242
242
|
return filteredVersions;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
export function getAllOptionsAfterCurrentVersion(store, versions, currentVersion, defaultVersion) {
|
|
245
|
+
export function getAllOptionsAfterCurrentVersion(store, versions, currentVersion, defaultVersion, manual = false) {
|
|
246
246
|
const out = (versions || []).filter((obj) => !!obj.serverArgs).map((obj) => {
|
|
247
247
|
let disabled = false;
|
|
248
248
|
let experimental = false;
|
|
@@ -260,6 +260,9 @@ export function getAllOptionsAfterCurrentVersion(store, versions, currentVersion
|
|
|
260
260
|
|
|
261
261
|
if (isCurrentVersion) {
|
|
262
262
|
label = `${ label } ${ store.getters['i18n/t']('cluster.kubernetesVersion.current') }`;
|
|
263
|
+
if (manual) {
|
|
264
|
+
label = `${ label } ${ store.getters['i18n/t']('cluster.kubernetesVersion.manual') }`;
|
|
265
|
+
}
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
if (experimental) {
|
package/utils/cspAdaptor.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// For testing these could be changed to something like...
|
|
2
2
|
|
|
3
3
|
import { CATALOG } from '@shell/config/types';
|
|
4
|
+
import { ActionFindPageArgs, ActionFindPageTransientResponse } from '@shell/types/store/dashboard-store.types';
|
|
4
5
|
import { FilterArgs, PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
5
6
|
import { VuexStore } from '@shell/types/store/vuex';
|
|
6
7
|
|
|
7
8
|
const CSP_ADAPTER_APPS = ['rancher-csp-adapter', 'rancher-csp-billing-adapter'];
|
|
8
9
|
// For testing above line could be replaced with below line...
|
|
9
|
-
// const
|
|
10
|
+
// const CSP_ADAPTER_APPS = ['rancher-webhooka', 'rancher-webhook'];
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Helpers in order to
|
|
@@ -16,27 +17,44 @@ class CspAdapterUtils {
|
|
|
16
17
|
return $store.getters[`management/paginationEnabled`]({ id: CATALOG.APP, context: 'branding' });
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
static
|
|
20
|
+
private static apps?: any[] = undefined;
|
|
21
|
+
public static resetState() {
|
|
22
|
+
this.apps = undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static async fetchCspAdaptorApp($store: VuexStore): Promise<any> {
|
|
26
|
+
if (this.apps) {
|
|
27
|
+
return this.apps;
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
// For the login page, the schemas won't be loaded - we don't need the apps in this case
|
|
21
31
|
if ($store.getters['management/canList'](CATALOG.APP)) {
|
|
22
32
|
if (CspAdapterUtils.canPagination($store)) {
|
|
23
33
|
// Restrict the amount of apps we need to fetch
|
|
24
|
-
|
|
34
|
+
const opt: ActionFindPageArgs = {
|
|
35
|
+
pagination: new FilterArgs({
|
|
36
|
+
filters: PaginationParamFilter.createMultipleFields(CSP_ADAPTER_APPS.map(
|
|
37
|
+
(t) => new PaginationFilterField({
|
|
38
|
+
field: 'metadata.name',
|
|
39
|
+
value: t,
|
|
40
|
+
})
|
|
41
|
+
)),
|
|
42
|
+
}),
|
|
43
|
+
watch: false,
|
|
44
|
+
transient: true
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const resp: ActionFindPageTransientResponse = await $store.dispatch('management/findPage', {
|
|
25
48
|
type: CATALOG.APP,
|
|
26
|
-
opt
|
|
27
|
-
pagination: new FilterArgs({
|
|
28
|
-
filters: PaginationParamFilter.createMultipleFields(CSP_ADAPTER_APPS.map(
|
|
29
|
-
(t) => new PaginationFilterField({
|
|
30
|
-
field: 'metadata.name',
|
|
31
|
-
value: t,
|
|
32
|
-
})
|
|
33
|
-
)),
|
|
34
|
-
})
|
|
35
|
-
}
|
|
49
|
+
opt
|
|
36
50
|
});
|
|
51
|
+
|
|
52
|
+
this.apps = resp.data;
|
|
53
|
+
} else {
|
|
54
|
+
this.apps = await $store.dispatch('management/findAll', { type: CATALOG.APP });
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
return
|
|
57
|
+
return this.apps;
|
|
40
58
|
}
|
|
41
59
|
|
|
42
60
|
return Promise.resolve([]);
|
package/utils/fleet.ts
CHANGED
|
@@ -184,7 +184,7 @@ class Fleet {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
detailLocation(r: Resource, mgmtClusterName: string): any {
|
|
187
|
-
|
|
187
|
+
const location = mapStateToEnum(r.state) === STATES_ENUM.MISSING ? undefined : {
|
|
188
188
|
name: `c-cluster-product-resource${ r.namespace ? '-namespace' : '' }-id`,
|
|
189
189
|
params: {
|
|
190
190
|
product: EXPLORER_NAME,
|
|
@@ -194,6 +194,13 @@ class Fleet {
|
|
|
194
194
|
id: r.name,
|
|
195
195
|
},
|
|
196
196
|
};
|
|
197
|
+
|
|
198
|
+
// Having an undefined param can yield a console warning like [Vue Router warn]: Discarded invalid param(s) "namespace" when navigating
|
|
199
|
+
if (location && !location.params.namespace) {
|
|
200
|
+
delete location.params.namespace;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return location;
|
|
197
204
|
}
|
|
198
205
|
|
|
199
206
|
/**
|
|
@@ -165,7 +165,7 @@ class PaginationUtils {
|
|
|
165
165
|
/**
|
|
166
166
|
* Is pagination enabled at a global level or for a specific resource
|
|
167
167
|
*/
|
|
168
|
-
isEnabled({ rootGetters, $
|
|
168
|
+
isEnabled({ rootGetters, $extension }: any, enabledFor: PaginationResourceContext) {
|
|
169
169
|
// Cache must be enabled to support pagination api
|
|
170
170
|
if (!this.isSteveCacheEnabled({ rootGetters })) {
|
|
171
171
|
return false;
|
|
@@ -184,7 +184,7 @@ class PaginationUtils {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// Does an extension say this type is enabled?
|
|
187
|
-
const plugin = $
|
|
187
|
+
const plugin = $extension as ExtensionManager;
|
|
188
188
|
const paginationExtensionPoints = plugin.getAll()[EXT_IDS.SERVER_SIDE_PAGINATION_RESOURCES];
|
|
189
189
|
|
|
190
190
|
if (paginationExtensionPoints) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import paginationUtils from '@shell/utils/pagination-utils';
|
|
2
2
|
import { PaginationArgs, PaginationResourceContext } from '@shell/types/store/pagination.types';
|
|
3
3
|
import { VuexStore } from '@shell/types/store/vuex';
|
|
4
|
-
import { ActionFindPageArgs,
|
|
4
|
+
import { ActionFindPageArgs, ActionFindPageTransientResponse } from '@shell/types/store/dashboard-store.types';
|
|
5
5
|
import { STEVE_WATCH_EVENT_TYPES, STEVE_WATCH_MODE } from '@shell/types/store/subscribe.types';
|
|
6
6
|
import { Reactive, reactive } from 'vue';
|
|
7
7
|
import { STEVE_UNWATCH_EVENT_PARAMS, STEVE_WATCH_EVENT_LISTENER_CALLBACK, STEVE_WATCH_EVENT_PARAMS, STEVE_WATCH_EVENT_PARAMS_COMMON } from '@shell/types/store/subscribe-events.types';
|
|
@@ -30,7 +30,7 @@ interface Args {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
interface Result<T> extends Omit<
|
|
33
|
+
interface Result<T> extends Omit<ActionFindPageTransientResponse<T>, 'data'> {
|
|
34
34
|
data: Reactive<T[]>
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -69,7 +69,7 @@ class PaginationWrapper<T extends object> {
|
|
|
69
69
|
this.classify = formatResponse?.classify || false;
|
|
70
70
|
this.reactive = formatResponse?.reactive || false;
|
|
71
71
|
|
|
72
|
-
this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $
|
|
72
|
+
this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $extension: this.$store.$extension }, enabledFor);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
async request({ pagination, forceWatch }: {
|
|
@@ -86,7 +86,7 @@ class PaginationWrapper<T extends object> {
|
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
// Fetch
|
|
89
|
-
const out:
|
|
89
|
+
const out: ActionFindPageTransientResponse<T> = await this.$store.dispatch(`${ this.enabledFor.store }/findPage`, { opt, type: this.enabledFor.resource?.id });
|
|
90
90
|
|
|
91
91
|
// Watch
|
|
92
92
|
const firstTime = !this.steveWatchParams;
|
package/utils/router.js
CHANGED
|
@@ -117,3 +117,53 @@ export function findMeta(route, key) {
|
|
|
117
117
|
|
|
118
118
|
return undefined;
|
|
119
119
|
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Find a route definition given a routeName
|
|
123
|
+
* @param {*} router VueRouter instance
|
|
124
|
+
* @param {*} routeName the name we want to look up
|
|
125
|
+
* @returns the route definition or undefined if it wasn't found
|
|
126
|
+
*/
|
|
127
|
+
export function findRouteDefinitionByName(router, routeName) {
|
|
128
|
+
const routes = router.getRoutes();
|
|
129
|
+
|
|
130
|
+
return routes.find((r) => r.name === routeName);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Looks for the route definition and then ensures there's only valid params
|
|
135
|
+
* @param {*} router VueRouter instance
|
|
136
|
+
* @param {*} routeRecord an object conforming to the Route Record interface
|
|
137
|
+
* @returns the passed in routeLocation with only valid params.
|
|
138
|
+
*/
|
|
139
|
+
export function filterLocationValidParams(router, routeRecord) {
|
|
140
|
+
if (!routeRecord || !routeRecord.name || !routeRecord.params) {
|
|
141
|
+
console.warn('filterLocationValidParams received invalid arguments'); // eslint-disable-line no-console
|
|
142
|
+
|
|
143
|
+
return routeRecord;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const routeDefinition = findRouteDefinitionByName(router, routeRecord.name);
|
|
147
|
+
|
|
148
|
+
if (!routeDefinition) {
|
|
149
|
+
console.warn('Could not find a route definition given the routeRecord', routeRecord); // eslint-disable-line no-console
|
|
150
|
+
|
|
151
|
+
return routeRecord;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const specifiedParams = routeRecord.params;
|
|
155
|
+
const validParams = {};
|
|
156
|
+
|
|
157
|
+
Object.entries(specifiedParams).forEach(([key, value]) => {
|
|
158
|
+
const pathParam = `:${ key }`;
|
|
159
|
+
|
|
160
|
+
if (routeDefinition.path.includes(pathParam)) {
|
|
161
|
+
validParams[key] = value;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
...routeRecord,
|
|
167
|
+
params: validParams
|
|
168
|
+
};
|
|
169
|
+
}
|
|
@@ -163,14 +163,14 @@ describe('pagination-utils', () => {
|
|
|
163
163
|
|
|
164
164
|
it('should return false if steve cache is disabled', () => {
|
|
165
165
|
mockRootGetters['features/get'].mockImplementation((feature: string) => feature === STEVE_CACHE ? false : undefined);
|
|
166
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
166
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, enabledFor);
|
|
167
167
|
|
|
168
168
|
expect(result).toBe(false);
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
it('should return false if pagination settings are not defined', () => {
|
|
172
172
|
jest.spyOn(paginationUtils, 'getSettings').mockReturnValue(undefined);
|
|
173
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
173
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, enabledFor);
|
|
174
174
|
|
|
175
175
|
expect(result).toBe(false);
|
|
176
176
|
|
|
@@ -189,7 +189,7 @@ describe('pagination-utils', () => {
|
|
|
189
189
|
return null;
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
192
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, undefined as unknown as PaginationResourceContext);
|
|
193
193
|
|
|
194
194
|
expect(result).toBe(false);
|
|
195
195
|
});
|
|
@@ -200,7 +200,7 @@ describe('pagination-utils', () => {
|
|
|
200
200
|
|
|
201
201
|
mockPlugin.getAll.mockReturnValue({ [EXT_IDS.SERVER_SIDE_PAGINATION_RESOURCES]: { 'my-ext': () => extensionSettings } });
|
|
202
202
|
|
|
203
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
203
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, enabledFor);
|
|
204
204
|
|
|
205
205
|
expect(result).toBe(true);
|
|
206
206
|
});
|
|
@@ -215,7 +215,7 @@ describe('pagination-utils', () => {
|
|
|
215
215
|
// Mocking PAGINATION_SETTINGS_STORE_DEFAULTS behavior
|
|
216
216
|
jest.spyOn(paginationUtils, 'getStoreDefault').mockReturnValue(defaultSettings);
|
|
217
217
|
|
|
218
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
218
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, enabledFor);
|
|
219
219
|
|
|
220
220
|
expect(result).toBe(true);
|
|
221
221
|
});
|
|
@@ -235,7 +235,7 @@ describe('pagination-utils', () => {
|
|
|
235
235
|
// Mocking PAGINATION_SETTINGS_STORE_DEFAULTS behavior
|
|
236
236
|
jest.spyOn(paginationUtils, 'getStoreDefault').mockReturnValue({ cluster: { resources: { enableAll: true } } });
|
|
237
237
|
|
|
238
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
238
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, enabledFor);
|
|
239
239
|
|
|
240
240
|
expect(result).toBe(true);
|
|
241
241
|
});
|
|
@@ -255,7 +255,7 @@ describe('pagination-utils', () => {
|
|
|
255
255
|
return null;
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
258
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, enabledFor);
|
|
259
259
|
|
|
260
260
|
expect(result).toBe(true);
|
|
261
261
|
});
|
|
@@ -275,7 +275,7 @@ describe('pagination-utils', () => {
|
|
|
275
275
|
return null;
|
|
276
276
|
});
|
|
277
277
|
|
|
278
|
-
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $
|
|
278
|
+
const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $extension: mockPlugin }, enabledFor);
|
|
279
279
|
|
|
280
280
|
expect(result).toBe(false);
|
|
281
281
|
});
|
package/vue.config.js
CHANGED
|
@@ -323,16 +323,16 @@ const getVirtualModules = (dir, includePkg) => {
|
|
|
323
323
|
|
|
324
324
|
// Package file must have rancher field to be a plugin
|
|
325
325
|
if (includePkg(name) && library.rancher) {
|
|
326
|
-
reqs += `$
|
|
326
|
+
reqs += `$extension.registerBuiltinExtension('${ name }', require(\'~/pkg/${ name }\')); `;
|
|
327
327
|
}
|
|
328
328
|
});
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
Object.keys(librariesIndex).forEach((i) => {
|
|
332
|
-
reqs += `$
|
|
332
|
+
reqs += `$extension.loadAsync('${ i }', '/pkg/${ i }/${ librariesIndex[i] }');`;
|
|
333
333
|
});
|
|
334
334
|
|
|
335
|
-
return new VirtualModulesPlugin({ 'node_modules/@rancher/dynamic.js': `export default function ($
|
|
335
|
+
return new VirtualModulesPlugin({ 'node_modules/@rancher/dynamic.js': `export default function ($extension) { ${ reqs } };` });
|
|
336
336
|
};
|
|
337
337
|
|
|
338
338
|
const getAutoImport = () => new webpack.NormalModuleReplacementPlugin(/^@rancher\/auto-import$/, (resource) => {
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ExtensionManager } from '@shell/types/extension-manager';
|
|
2
|
-
import { getExtensionManager } from '@shell/core/extension-manager-impl';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Provides access to the registered extension manager instance. Used within Vue
|
|
6
|
-
* components or other composables that require extension functionality.
|
|
7
|
-
* @returns The extension manager instance
|
|
8
|
-
*/
|
|
9
|
-
export const useExtensionManager = (): ExtensionManager => {
|
|
10
|
-
const extension = getExtensionManager();
|
|
11
|
-
|
|
12
|
-
if (!extension) {
|
|
13
|
-
throw new Error('useExtensionManager must be called after the extensionManager has been initialized');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return extension;
|
|
17
|
-
};
|