@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.3
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 +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +22 -5
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +0 -10
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/Wizard.vue +14 -13
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +37 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/LabeledSelect.vue +20 -3
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/templates/home.vue +18 -0
- package/components/templates/plain.vue +18 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/router/routes.js +6 -0
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +107 -7
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +111 -1
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +57 -20
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/azuread.vue +122 -14
- package/edit/auth/oidc.vue +2 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +12 -8
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +3 -1
- package/models/management.cattle.io.cluster.js +417 -40
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +11 -29
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/chart.vue +60 -8
- package/pages/c/_cluster/apps/charts/install.vue +10 -7
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
- package/pages/c/_cluster/uiplugins/index.vue +203 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +27 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +102 -2
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +418 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable jest/max-nested-describe */
|
|
2
2
|
|
|
3
|
-
import { TYPE_MODES, getters } from '../type-map';
|
|
3
|
+
import { TYPE_MODES, getters, mutations, DSL } from '../type-map';
|
|
4
4
|
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
5
5
|
import {
|
|
6
6
|
COUNT,
|
|
@@ -1341,4 +1341,559 @@ describe('type-map', () => {
|
|
|
1341
1341
|
expect(getters.groupLabel(state)('nonexistent')).toBeUndefined();
|
|
1342
1342
|
});
|
|
1343
1343
|
});
|
|
1344
|
+
|
|
1345
|
+
describe('configureType - product scoping', () => {
|
|
1346
|
+
describe('mutations.configureType', () => {
|
|
1347
|
+
it('should scope typeOptions by product', () => {
|
|
1348
|
+
const state = { typeOptions: {} } as any;
|
|
1349
|
+
|
|
1350
|
+
mutations.configureType(state, {
|
|
1351
|
+
product: 'product-1',
|
|
1352
|
+
match: 'pod',
|
|
1353
|
+
customRoute: { name: 'product-1-route' }
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
mutations.configureType(state, {
|
|
1357
|
+
product: 'explorer',
|
|
1358
|
+
match: 'pod',
|
|
1359
|
+
customRoute: { name: 'explorer-route' }
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
expect(state.typeOptions['product-1']).toHaveLength(1);
|
|
1363
|
+
expect(state.typeOptions['product-1'][0].customRoute.name).toBe('product-1-route');
|
|
1364
|
+
expect(state.typeOptions['explorer']).toHaveLength(1);
|
|
1365
|
+
expect(state.typeOptions['explorer'][0].customRoute.name).toBe('explorer-route');
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
it('should merge multiple configures for same type in same product', () => {
|
|
1369
|
+
const state = { typeOptions: {} } as any;
|
|
1370
|
+
|
|
1371
|
+
mutations.configureType(state, {
|
|
1372
|
+
product: 'product-1',
|
|
1373
|
+
match: 'pod',
|
|
1374
|
+
isCreatable: false
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
mutations.configureType(state, {
|
|
1378
|
+
product: 'product-1',
|
|
1379
|
+
match: 'pod',
|
|
1380
|
+
customRoute: { name: 'custom' }
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
expect(state.typeOptions['product-1']).toHaveLength(1);
|
|
1384
|
+
expect(state.typeOptions['product-1'][0].isCreatable).toBe(false);
|
|
1385
|
+
expect(state.typeOptions['product-1'][0].customRoute.name).toBe('custom');
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
it('should merge custom data objects when configuring same type multiple times', () => {
|
|
1389
|
+
const state = { typeOptions: {} } as any;
|
|
1390
|
+
|
|
1391
|
+
mutations.configureType(state, {
|
|
1392
|
+
product: 'product-1',
|
|
1393
|
+
match: 'pod',
|
|
1394
|
+
custom: { setting1: 'value1' }
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
mutations.configureType(state, {
|
|
1398
|
+
product: 'product-1',
|
|
1399
|
+
match: 'pod',
|
|
1400
|
+
custom: { setting2: 'value2' }
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
expect(state.typeOptions['product-1'][0].custom).toStrictEqual({
|
|
1404
|
+
setting1: 'value1',
|
|
1405
|
+
setting2: 'value2'
|
|
1406
|
+
});
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
it('should throw error when product parameter is missing in new format (2.15+)', () => {
|
|
1410
|
+
const state = { typeOptions: {} } as any;
|
|
1411
|
+
|
|
1412
|
+
expect(() => {
|
|
1413
|
+
mutations.configureType(state, {
|
|
1414
|
+
match: 'pod',
|
|
1415
|
+
customRoute: { name: 'route' }
|
|
1416
|
+
});
|
|
1417
|
+
}).toThrow(/product parameter is required/);
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
it('should throw error with type info when product parameter is missing', () => {
|
|
1421
|
+
const state = { typeOptions: {} } as any;
|
|
1422
|
+
|
|
1423
|
+
expect(() => {
|
|
1424
|
+
mutations.configureType(state, {
|
|
1425
|
+
match: 'pod',
|
|
1426
|
+
customRoute: { name: 'route' }
|
|
1427
|
+
});
|
|
1428
|
+
}).toThrow(/for type/);
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
it('should allow different types in same product', () => {
|
|
1432
|
+
const state = { typeOptions: {} } as any;
|
|
1433
|
+
|
|
1434
|
+
mutations.configureType(state, {
|
|
1435
|
+
product: 'product-1',
|
|
1436
|
+
match: 'pod',
|
|
1437
|
+
customRoute: { name: 'pod-route' }
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
mutations.configureType(state, {
|
|
1441
|
+
product: 'product-1',
|
|
1442
|
+
match: 'deployment',
|
|
1443
|
+
customRoute: { name: 'deployment-route' }
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
expect(state.typeOptions['product-1']).toHaveLength(2);
|
|
1447
|
+
expect(state.typeOptions['product-1'][0].customRoute.name).toBe('pod-route');
|
|
1448
|
+
expect(state.typeOptions['product-1'][1].customRoute.name).toBe('deployment-route');
|
|
1449
|
+
});
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
describe('getters.optionsFor - product filtering', () => {
|
|
1453
|
+
it('should return product-scoped options', () => {
|
|
1454
|
+
const state = {
|
|
1455
|
+
typeOptions: {
|
|
1456
|
+
'product-1': [{ match: 'pod', customRoute: { name: 'product-1-route' } }],
|
|
1457
|
+
explorer: [{ match: 'pod', customRoute: { name: 'explorer-route' } }]
|
|
1458
|
+
}
|
|
1459
|
+
} as any;
|
|
1460
|
+
|
|
1461
|
+
const rootGetters = { productId: 'product-1' };
|
|
1462
|
+
|
|
1463
|
+
const optionsFn = getters.optionsFor(state, {}, {}, rootGetters);
|
|
1464
|
+
const opts = optionsFn('pod', false);
|
|
1465
|
+
|
|
1466
|
+
expect(opts.customRoute.name).toBe('product-1-route');
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
it('should return defaults when type not configured in current product', () => {
|
|
1470
|
+
const state = { typeOptions: { explorer: [{ match: 'pod', customRoute: { name: 'explorer-route' } }] } } as any;
|
|
1471
|
+
|
|
1472
|
+
const rootGetters = { productId: 'product-1' };
|
|
1473
|
+
|
|
1474
|
+
const optionsFn = getters.optionsFor(state, {}, {}, rootGetters);
|
|
1475
|
+
const opts = optionsFn('pod', false);
|
|
1476
|
+
|
|
1477
|
+
expect(opts.customRoute).toBeUndefined();
|
|
1478
|
+
expect(opts.isCreatable).toBe(true); // default value
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
it('should handle missing product gracefully', () => {
|
|
1482
|
+
const state = { typeOptions: {} } as any;
|
|
1483
|
+
const rootGetters = { productId: 'nonexistent' };
|
|
1484
|
+
|
|
1485
|
+
const optionsFn = getters.optionsFor(state, {}, {}, rootGetters);
|
|
1486
|
+
const opts = optionsFn('pod', false);
|
|
1487
|
+
|
|
1488
|
+
expect(opts.isCreatable).toBe(true); // defaults
|
|
1489
|
+
expect(opts.customRoute).toBeUndefined();
|
|
1490
|
+
});
|
|
1491
|
+
|
|
1492
|
+
it('should not apply explorer product config to other products', () => {
|
|
1493
|
+
const state = {
|
|
1494
|
+
typeOptions: {
|
|
1495
|
+
explorer: [{ match: 'pod', customRoute: { name: 'explorer-pod-route' } }],
|
|
1496
|
+
'product-1': [{ match: 'pod', customRoute: { name: 'product-1-pod-route' } }]
|
|
1497
|
+
}
|
|
1498
|
+
} as any;
|
|
1499
|
+
|
|
1500
|
+
// Check explorer product
|
|
1501
|
+
const explorerOptions = getters.optionsFor(state, {}, {}, { productId: 'explorer' });
|
|
1502
|
+
|
|
1503
|
+
expect(explorerOptions('pod', false).customRoute.name).toBe('explorer-pod-route');
|
|
1504
|
+
|
|
1505
|
+
// Check other product
|
|
1506
|
+
const product1Options = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1507
|
+
|
|
1508
|
+
expect(product1Options('pod', false).customRoute.name).toBe('product-1-pod-route');
|
|
1509
|
+
|
|
1510
|
+
// Check unconfigured product
|
|
1511
|
+
const product2Options = getters.optionsFor(state, {}, {}, { productId: 'product-2' });
|
|
1512
|
+
|
|
1513
|
+
expect(product2Options('pod', false).customRoute).toBeUndefined();
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
it('should use explicit product parameter when provided', () => {
|
|
1517
|
+
const state = {
|
|
1518
|
+
typeOptions: {
|
|
1519
|
+
explorer: [
|
|
1520
|
+
{
|
|
1521
|
+
match: 'pod',
|
|
1522
|
+
customRoute: { name: 'explorer-route' },
|
|
1523
|
+
localOnly: true
|
|
1524
|
+
}
|
|
1525
|
+
],
|
|
1526
|
+
'product-1': [
|
|
1527
|
+
{
|
|
1528
|
+
match: 'pod',
|
|
1529
|
+
customRoute: { name: 'product-1-route' },
|
|
1530
|
+
localOnly: false
|
|
1531
|
+
}
|
|
1532
|
+
]
|
|
1533
|
+
}
|
|
1534
|
+
} as any;
|
|
1535
|
+
|
|
1536
|
+
const optionsFn = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1537
|
+
|
|
1538
|
+
// Without product override, should use current product (product-1)
|
|
1539
|
+
const defaultOpts = optionsFn('pod', false);
|
|
1540
|
+
|
|
1541
|
+
expect(defaultOpts.customRoute.name).toBe('product-1-route');
|
|
1542
|
+
expect(defaultOpts.localOnly).toBe(false);
|
|
1543
|
+
|
|
1544
|
+
// With product override to explorer, should use explorer's config
|
|
1545
|
+
const explorerOpts = optionsFn('pod', false, 'explorer');
|
|
1546
|
+
|
|
1547
|
+
expect(explorerOpts.customRoute.name).toBe('explorer-route');
|
|
1548
|
+
expect(explorerOpts.localOnly).toBe(true);
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
it('should handle product override for unconfigured types', () => {
|
|
1552
|
+
const state = {
|
|
1553
|
+
typeOptions: {
|
|
1554
|
+
explorer: [
|
|
1555
|
+
{
|
|
1556
|
+
match: 'pod',
|
|
1557
|
+
customRoute: { name: 'explorer-route' }
|
|
1558
|
+
}
|
|
1559
|
+
]
|
|
1560
|
+
}
|
|
1561
|
+
} as any;
|
|
1562
|
+
|
|
1563
|
+
const optionsFn = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1564
|
+
|
|
1565
|
+
// Override to explorer which has config
|
|
1566
|
+
const explorerOpts = optionsFn('secret', false, 'explorer');
|
|
1567
|
+
|
|
1568
|
+
expect(explorerOpts.customRoute).toBeUndefined(); // secret not configured in explorer
|
|
1569
|
+
expect(explorerOpts.isCreatable).toBe(true); // uses default
|
|
1570
|
+
|
|
1571
|
+
// Override to product-2 which has no config
|
|
1572
|
+
const product2Opts = optionsFn('pod', false, 'product-2');
|
|
1573
|
+
|
|
1574
|
+
expect(product2Opts.customRoute).toBeUndefined();
|
|
1575
|
+
expect(product2Opts.isCreatable).toBe(true);
|
|
1576
|
+
});
|
|
1577
|
+
|
|
1578
|
+
describe('backward compatibility - legacy array format (Rancher 2.14)', () => {
|
|
1579
|
+
it('should handle legacy array format with product field', () => {
|
|
1580
|
+
const state = {
|
|
1581
|
+
typeOptions: [
|
|
1582
|
+
{
|
|
1583
|
+
match: 'pod', product: 'explorer', customRoute: { name: 'explorer-route' }
|
|
1584
|
+
},
|
|
1585
|
+
{
|
|
1586
|
+
match: 'pod', product: 'product-1', customRoute: { name: 'product-1-route' }
|
|
1587
|
+
}
|
|
1588
|
+
]
|
|
1589
|
+
} as any;
|
|
1590
|
+
|
|
1591
|
+
const explorerOptions = getters.optionsFor(state, {}, {}, { productId: 'explorer' });
|
|
1592
|
+
const product1Options = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1593
|
+
|
|
1594
|
+
expect(explorerOptions('pod', false).customRoute.name).toBe('explorer-route');
|
|
1595
|
+
expect(product1Options('pod', false).customRoute.name).toBe('product-1-route');
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
it('should handle legacy array format with entries without product field', () => {
|
|
1599
|
+
const state = {
|
|
1600
|
+
typeOptions: [
|
|
1601
|
+
{
|
|
1602
|
+
match: 'pod', product: 'explorer', customRoute: { name: 'explorer-route' }
|
|
1603
|
+
},
|
|
1604
|
+
{ match: 'pod', customRoute: { name: 'shared-route' } }
|
|
1605
|
+
]
|
|
1606
|
+
} as any;
|
|
1607
|
+
|
|
1608
|
+
const explorerOptions = getters.optionsFor(state, {}, {}, { productId: 'explorer' });
|
|
1609
|
+
const otherProductOptions = getters.optionsFor(state, {}, {}, { productId: 'other' });
|
|
1610
|
+
|
|
1611
|
+
// explorer gets its product-specific entry (matches first)
|
|
1612
|
+
expect(explorerOptions('pod', false).customRoute.name).toBe('explorer-route');
|
|
1613
|
+
|
|
1614
|
+
// other product gets only unscoped entries
|
|
1615
|
+
expect(otherProductOptions('pod', false).customRoute.name).toBe('shared-route');
|
|
1616
|
+
});
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
describe('backward compatibility - missing product parameter (old extensions in 2.15)', () => {
|
|
1620
|
+
it('should reject unscoped config (requires product parameter in 2.15+)', () => {
|
|
1621
|
+
const state = { typeOptions: {} } as any;
|
|
1622
|
+
|
|
1623
|
+
expect(() => {
|
|
1624
|
+
mutations.configureType(state, {
|
|
1625
|
+
match: 'pod',
|
|
1626
|
+
customRoute: { name: 'legacy-route' }
|
|
1627
|
+
});
|
|
1628
|
+
}).toThrow(/product parameter is required/);
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
it('should retrieve legacyCompatibilityProdRegistration entries for products without specific config', () => {
|
|
1632
|
+
const state = {
|
|
1633
|
+
typeOptions: {
|
|
1634
|
+
'product-1': [{ match: 'pod', customRoute: { name: 'product-1-route' } }],
|
|
1635
|
+
legacyCompatibilityProdRegistration: [{ match: 'pod', customRoute: { name: 'legacy-route' } }]
|
|
1636
|
+
}
|
|
1637
|
+
} as any;
|
|
1638
|
+
|
|
1639
|
+
const product1Options = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1640
|
+
const product2Options = getters.optionsFor(state, {}, {}, { productId: 'product-2' });
|
|
1641
|
+
|
|
1642
|
+
// product-1 gets its own config (takes precedence)
|
|
1643
|
+
expect(product1Options('pod', false).customRoute.name).toBe('product-1-route');
|
|
1644
|
+
|
|
1645
|
+
// product-2 gets legacy entries
|
|
1646
|
+
expect(product2Options('pod', false).customRoute.name).toBe('legacy-route');
|
|
1647
|
+
});
|
|
1648
|
+
|
|
1649
|
+
it('should merge product-specific and legacyCompatibilityProdRegistration buckets in getter', () => {
|
|
1650
|
+
const state = {
|
|
1651
|
+
typeOptions: {
|
|
1652
|
+
'product-1': [{ match: 'deployment', customRoute: { name: 'product-1-deployment-route' } }],
|
|
1653
|
+
legacyCompatibilityProdRegistration: [{ match: 'pod', customRoute: { name: 'legacy-pod-route' } }]
|
|
1654
|
+
}
|
|
1655
|
+
} as any;
|
|
1656
|
+
|
|
1657
|
+
const product1Options = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1658
|
+
|
|
1659
|
+
// pod from legacy bucket (product-1 doesn't have pod config)
|
|
1660
|
+
expect(product1Options('pod', false).customRoute.name).toBe('legacy-pod-route');
|
|
1661
|
+
|
|
1662
|
+
// deployment from product-specific bucket
|
|
1663
|
+
expect(product1Options('deployment', false).customRoute.name).toBe('product-1-deployment-route');
|
|
1664
|
+
});
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
describe('mixed extensions scenario (2.15 with old + new extensions)', () => {
|
|
1668
|
+
it('should handle old extension (2.14 shell) + new extension (2.15 shell) in same product', () => {
|
|
1669
|
+
const state = {
|
|
1670
|
+
typeOptions: {
|
|
1671
|
+
explorer: [{ match: 'pod', customRoute: { name: 'explorer-pod-route' } }],
|
|
1672
|
+
legacyCompatibilityProdRegistration: [
|
|
1673
|
+
{ match: 'secret', customRoute: { name: 'legacy-secret-route' } },
|
|
1674
|
+
{ match: 'configmap', customRoute: { name: 'legacy-configmap-route' } }
|
|
1675
|
+
]
|
|
1676
|
+
}
|
|
1677
|
+
} as any;
|
|
1678
|
+
|
|
1679
|
+
const explorerOptions = getters.optionsFor(state, {}, {}, { productId: 'explorer' });
|
|
1680
|
+
|
|
1681
|
+
// New extension config (product-specific)
|
|
1682
|
+
expect(explorerOptions('pod', false).customRoute.name).toBe('explorer-pod-route');
|
|
1683
|
+
|
|
1684
|
+
// Old extension configs (from legacy bucket)
|
|
1685
|
+
expect(explorerOptions('secret', false).customRoute.name).toBe('legacy-secret-route');
|
|
1686
|
+
expect(explorerOptions('configmap', false).customRoute.name).toBe('legacy-configmap-route');
|
|
1687
|
+
});
|
|
1688
|
+
|
|
1689
|
+
it('should prioritize product-specific config over legacy bucket when same type in both', () => {
|
|
1690
|
+
const state = {
|
|
1691
|
+
typeOptions: {
|
|
1692
|
+
'product-1': [{ match: 'pod', customRoute: { name: 'product-1-route' } }],
|
|
1693
|
+
legacyCompatibilityProdRegistration: [{ match: 'pod', customRoute: { name: 'legacy-route' } }]
|
|
1694
|
+
}
|
|
1695
|
+
} as any;
|
|
1696
|
+
|
|
1697
|
+
const product1Options = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1698
|
+
|
|
1699
|
+
// product-specific should come first in the array and match first
|
|
1700
|
+
expect(product1Options('pod', false).customRoute.name).toBe('product-1-route');
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
it('should work with multiple products having both specific and legacy configs', () => {
|
|
1704
|
+
const state = {
|
|
1705
|
+
typeOptions: {
|
|
1706
|
+
'product-1': [{ match: 'pod', customRoute: { name: 'p1-pod' } }],
|
|
1707
|
+
'product-2': [{ match: 'pod', customRoute: { name: 'p2-pod' } }],
|
|
1708
|
+
legacyCompatibilityProdRegistration: [
|
|
1709
|
+
{ match: 'secret', customRoute: { name: 'legacy-secret' } },
|
|
1710
|
+
{ match: 'pod', customRoute: { name: 'legacy-pod' } }
|
|
1711
|
+
]
|
|
1712
|
+
}
|
|
1713
|
+
} as any;
|
|
1714
|
+
|
|
1715
|
+
const p1Options = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1716
|
+
const p2Options = getters.optionsFor(state, {}, {}, { productId: 'product-2' });
|
|
1717
|
+
const p3Options = getters.optionsFor(state, {}, {}, { productId: 'product-3' });
|
|
1718
|
+
|
|
1719
|
+
// product-1: gets its own pod config, ignores legacy pod, gets legacy secret
|
|
1720
|
+
expect(p1Options('pod', false).customRoute.name).toBe('p1-pod');
|
|
1721
|
+
expect(p1Options('secret', false).customRoute.name).toBe('legacy-secret');
|
|
1722
|
+
|
|
1723
|
+
// product-2: gets its own pod config, ignores legacy pod, gets legacy secret
|
|
1724
|
+
expect(p2Options('pod', false).customRoute.name).toBe('p2-pod');
|
|
1725
|
+
expect(p2Options('secret', false).customRoute.name).toBe('legacy-secret');
|
|
1726
|
+
|
|
1727
|
+
// product-3: gets only legacy configs
|
|
1728
|
+
expect(p3Options('pod', false).customRoute.name).toBe('legacy-pod');
|
|
1729
|
+
expect(p3Options('secret', false).customRoute.name).toBe('legacy-secret');
|
|
1730
|
+
});
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1733
|
+
describe('product name validation', () => {
|
|
1734
|
+
it('should throw error when product name equals reserved legacyCompatibilityProdRegistration', () => {
|
|
1735
|
+
const state = { typeOptions: {} } as any;
|
|
1736
|
+
|
|
1737
|
+
expect(() => {
|
|
1738
|
+
mutations.configureType(state, {
|
|
1739
|
+
product: 'legacyCompatibilityProdRegistration',
|
|
1740
|
+
match: 'pod',
|
|
1741
|
+
customRoute: { name: 'route' }
|
|
1742
|
+
});
|
|
1743
|
+
}).toThrow(/cannot be "legacyCompatibilityProdRegistration"/);
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
it('should allow empty string in getter (falsy, bypasses validation)', () => {
|
|
1747
|
+
const state = { typeOptions: { 'product-1': [{ match: 'pod', customRoute: { name: 'p1-route' } }] } } as any;
|
|
1748
|
+
const optionsFn = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1749
|
+
|
|
1750
|
+
// Empty string is falsy so it skips validation and uses current product context
|
|
1751
|
+
expect(() => {
|
|
1752
|
+
optionsFn('pod', false, '');
|
|
1753
|
+
}).not.toThrow();
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
it('should throw error in getter when product override is the reserved name', () => {
|
|
1757
|
+
const state = { typeOptions: { 'product-1': [] } } as any;
|
|
1758
|
+
const optionsFn = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1759
|
+
|
|
1760
|
+
expect(() => {
|
|
1761
|
+
optionsFn('pod', false, 'legacyCompatibilityProdRegistration');
|
|
1762
|
+
}).toThrow(/cannot be "legacyCompatibilityProdRegistration"/);
|
|
1763
|
+
});
|
|
1764
|
+
|
|
1765
|
+
it('should allow undefined product (triggers default context in getter)', () => {
|
|
1766
|
+
const state = { typeOptions: { 'product-1': [{ match: 'pod', customRoute: { name: 'p1-route' } }] } } as any;
|
|
1767
|
+
const optionsFn = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1768
|
+
|
|
1769
|
+
// undefined product should use current product context
|
|
1770
|
+
expect(() => {
|
|
1771
|
+
optionsFn('pod', false, undefined);
|
|
1772
|
+
}).not.toThrow();
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
it('should allow valid product names', () => {
|
|
1776
|
+
const state = { typeOptions: {} } as any;
|
|
1777
|
+
|
|
1778
|
+
expect(() => {
|
|
1779
|
+
mutations.configureType(state, {
|
|
1780
|
+
product: 'my-product',
|
|
1781
|
+
match: 'pod',
|
|
1782
|
+
customRoute: { name: 'route' }
|
|
1783
|
+
});
|
|
1784
|
+
}).not.toThrow();
|
|
1785
|
+
|
|
1786
|
+
expect(state.typeOptions['my-product']).toHaveLength(1);
|
|
1787
|
+
});
|
|
1788
|
+
});
|
|
1789
|
+
|
|
1790
|
+
describe('edge cases and potential issues', () => {
|
|
1791
|
+
it('should handle explicit product override with product parameter in getter', () => {
|
|
1792
|
+
const state = {
|
|
1793
|
+
typeOptions: {
|
|
1794
|
+
'product-1': [{ match: 'pod', customRoute: { name: 'p1-route' } }],
|
|
1795
|
+
'product-2': [{ match: 'pod', customRoute: { name: 'p2-route' } }]
|
|
1796
|
+
}
|
|
1797
|
+
} as any;
|
|
1798
|
+
|
|
1799
|
+
const product1Options = getters.optionsFor(state, {}, {}, { productId: 'product-1' });
|
|
1800
|
+
|
|
1801
|
+
// Without override, should use product-1
|
|
1802
|
+
expect(product1Options('pod', false).customRoute.name).toBe('p1-route');
|
|
1803
|
+
|
|
1804
|
+
// With explicit override to product-2, should use product-2
|
|
1805
|
+
expect(product1Options('pod', false, 'product-2').customRoute.name).toBe('p2-route');
|
|
1806
|
+
});
|
|
1807
|
+
});
|
|
1808
|
+
|
|
1809
|
+
describe('dsl headers', () => {
|
|
1810
|
+
function createMockStore() {
|
|
1811
|
+
const committed: { type: string; payload: any }[] = [];
|
|
1812
|
+
|
|
1813
|
+
return {
|
|
1814
|
+
committed,
|
|
1815
|
+
commit(mutation: string, payload: any) {
|
|
1816
|
+
committed.push({ type: mutation, payload });
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
it('should commit only paginationHeaders when regular headers are not provided', () => {
|
|
1822
|
+
const mockStore = createMockStore();
|
|
1823
|
+
const { headers } = DSL(mockStore as any, 'test-product');
|
|
1824
|
+
const paginationHeaders = [
|
|
1825
|
+
{
|
|
1826
|
+
name: 'name', labelKey: 'tableHeaders.name', value: 'name', sort: ['name']
|
|
1827
|
+
}
|
|
1828
|
+
];
|
|
1829
|
+
|
|
1830
|
+
headers('pod', undefined as any, paginationHeaders);
|
|
1831
|
+
|
|
1832
|
+
expect(mockStore.committed).toHaveLength(1);
|
|
1833
|
+
expect(mockStore.committed[0].type).toBe('type-map/paginationHeaders');
|
|
1834
|
+
expect(mockStore.committed[0].payload).toStrictEqual({ type: 'pod', paginationHeaders });
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
it('should commit only paginationHeaders when regular headers are an empty array', () => {
|
|
1838
|
+
const mockStore = createMockStore();
|
|
1839
|
+
const { headers } = DSL(mockStore as any, 'test-product');
|
|
1840
|
+
const paginationHeaders = [
|
|
1841
|
+
{
|
|
1842
|
+
name: 'state', labelKey: 'tableHeaders.state', value: 'state', sort: ['state']
|
|
1843
|
+
}
|
|
1844
|
+
];
|
|
1845
|
+
|
|
1846
|
+
headers('pod', [], paginationHeaders);
|
|
1847
|
+
|
|
1848
|
+
expect(mockStore.committed).toHaveLength(1);
|
|
1849
|
+
expect(mockStore.committed[0].type).toBe('type-map/paginationHeaders');
|
|
1850
|
+
});
|
|
1851
|
+
|
|
1852
|
+
it('should commit both headers and paginationHeaders when both are provided', () => {
|
|
1853
|
+
const mockStore = createMockStore();
|
|
1854
|
+
const { headers } = DSL(mockStore as any, 'test-product');
|
|
1855
|
+
const regularHeaders = [
|
|
1856
|
+
{
|
|
1857
|
+
name: 'name', labelKey: 'tableHeaders.name', value: 'nameDisplay', sort: ['nameSort']
|
|
1858
|
+
}
|
|
1859
|
+
];
|
|
1860
|
+
const paginationHeaders = [
|
|
1861
|
+
{
|
|
1862
|
+
name: 'name', labelKey: 'tableHeaders.name', value: 'name', sort: ['name']
|
|
1863
|
+
}
|
|
1864
|
+
];
|
|
1865
|
+
|
|
1866
|
+
headers('pod', regularHeaders, paginationHeaders);
|
|
1867
|
+
|
|
1868
|
+
expect(mockStore.committed).toHaveLength(2);
|
|
1869
|
+
expect(mockStore.committed[0].type).toBe('type-map/headers');
|
|
1870
|
+
expect(mockStore.committed[1].type).toBe('type-map/paginationHeaders');
|
|
1871
|
+
});
|
|
1872
|
+
|
|
1873
|
+
it('should commit only regular headers when paginationHeaders are not provided', () => {
|
|
1874
|
+
const mockStore = createMockStore();
|
|
1875
|
+
const { headers } = DSL(mockStore as any, 'test-product');
|
|
1876
|
+
const regularHeaders = [
|
|
1877
|
+
{
|
|
1878
|
+
name: 'name', labelKey: 'tableHeaders.name', value: 'nameDisplay', sort: ['nameSort']
|
|
1879
|
+
}
|
|
1880
|
+
];
|
|
1881
|
+
|
|
1882
|
+
headers('pod', regularHeaders);
|
|
1883
|
+
|
|
1884
|
+
expect(mockStore.committed).toHaveLength(1);
|
|
1885
|
+
expect(mockStore.committed[0].type).toBe('type-map/headers');
|
|
1886
|
+
});
|
|
1887
|
+
|
|
1888
|
+
it('should not commit anything when both headers and paginationHeaders are empty', () => {
|
|
1889
|
+
const mockStore = createMockStore();
|
|
1890
|
+
const { headers } = DSL(mockStore as any, 'test-product');
|
|
1891
|
+
|
|
1892
|
+
headers('pod', [], []);
|
|
1893
|
+
|
|
1894
|
+
expect(mockStore.committed).toHaveLength(0);
|
|
1895
|
+
});
|
|
1896
|
+
});
|
|
1897
|
+
});
|
|
1898
|
+
});
|
|
1344
1899
|
});
|
package/store/action-menu.js
CHANGED
|
@@ -191,6 +191,9 @@ function _filter(map, disableAll = false) {
|
|
|
191
191
|
function _execute(resources, action, args, opts = {}) {
|
|
192
192
|
args = args || [];
|
|
193
193
|
if ( resources.length > 1 && action.bulkAction && !opts.alt ) {
|
|
194
|
+
// I think this is dead code. This is only run from the action menu where there's only ever a single resource
|
|
195
|
+
// For bulk actions shell/components/SortableTable/selection.js _execute runs
|
|
196
|
+
// If we do ever need to run this it should use that version
|
|
194
197
|
const fn = resources[0][action.bulkAction];
|
|
195
198
|
|
|
196
199
|
if ( fn ) {
|
|
@@ -203,14 +206,16 @@ function _execute(resources, action, args, opts = {}) {
|
|
|
203
206
|
for ( const resource of resources ) {
|
|
204
207
|
let fn;
|
|
205
208
|
|
|
209
|
+
const applyResource = action.altResource || resource;
|
|
210
|
+
|
|
206
211
|
if (opts.alt && action.altAction) {
|
|
207
|
-
fn =
|
|
212
|
+
fn = applyResource[action.altAction];
|
|
208
213
|
} else {
|
|
209
|
-
fn =
|
|
214
|
+
fn = applyResource[action.action];
|
|
210
215
|
}
|
|
211
216
|
|
|
212
217
|
if ( fn ) {
|
|
213
|
-
promises.push(fn.apply(
|
|
218
|
+
promises.push(fn.apply(applyResource, args));
|
|
214
219
|
}
|
|
215
220
|
}
|
|
216
221
|
|
package/store/auth.js
CHANGED
|
@@ -8,7 +8,7 @@ import { addParams, parse as parseUrl, removeParam } from '@shell/utils/url';
|
|
|
8
8
|
|
|
9
9
|
// configuration for Single Logout/SLO
|
|
10
10
|
// admissable auth providers compatible with SLO, based on shell/models/management.cattle.io.authconfig "configType"
|
|
11
|
-
export const SLO_AUTH_PROVIDERS = ['oidc', 'saml'];
|
|
11
|
+
export const SLO_AUTH_PROVIDERS = ['oidc', 'saml', 'oauth'];
|
|
12
12
|
|
|
13
13
|
// this is connected to the redirect url, for which the logic can be found in "shell/store/auth"
|
|
14
14
|
const SLO_TOKENS_ENDPOINT_LOGOUT_RES_BASETYPE = ['authConfigLogoutOutput'];
|