@rancher/shell 3.0.8-rc.8 → 3.0.8
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 +90 -0
- package/apis/intf/shell.ts +36 -0
- package/apis/intf/slide-in.ts +98 -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 +54 -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 +33 -0
- package/apis/shell/system.ts +65 -0
- package/apis/vue-shim.d.ts +11 -0
- package/assets/brand/suse/dark/rancher-logo.svg +1 -64
- package/assets/styles/global/_tooltip.scss +6 -1
- package/assets/translations/en-us.yaml +14 -1
- package/components/ActionMenuShell.vue +3 -1
- package/components/BackLink.vue +8 -0
- package/components/BannerGraphic.vue +1 -5
- package/components/BrandImage.vue +17 -6
- package/components/Cron/CronExpressionEditor.vue +1 -1
- package/components/Cron/CronExpressionEditorModal.vue +1 -1
- package/components/CruResource.vue +8 -1
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +1 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +50 -1
- package/components/Drawer/ResourceDetailDrawer/composables.ts +19 -0
- package/components/Drawer/ResourceDetailDrawer/index.vue +4 -1
- package/components/Drawer/ResourceDetailDrawer/types.ts +2 -1
- package/components/LocaleSelector.vue +2 -2
- package/components/ModalManager.vue +11 -1
- package/components/Questions/__tests__/Yaml.test.ts +1 -1
- package/components/Questions/__tests__/index.test.ts +159 -0
- package/components/RelatedResources.vue +5 -0
- package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
- package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
- package/components/Resource/Detail/Metadata/index.vue +3 -3
- package/components/Resource/Detail/ResourcePopover/index.vue +5 -1
- package/components/Resource/Detail/composables.ts +2 -2
- 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/__tests__/index.test.ts +86 -0
- package/components/Tabbed/index.vue +37 -2
- package/components/__tests__/NamespaceFilter.test.ts +49 -0
- package/components/auth/SelectPrincipal.vue +28 -6
- package/components/auth/__tests__/SelectPrincipal.test.ts +119 -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/InternalExternalIP.vue +4 -1
- package/components/formatter/LinkName.vue +5 -0
- package/components/formatter/__tests__/InternalExternalIP.test.ts +1 -1
- 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/components/templates/standalone.vue +1 -1
- package/composables/cruResource.ts +27 -0
- package/composables/focusTrap.ts +3 -1
- package/composables/resourceDetail.ts +15 -0
- package/composables/useI18n.ts +10 -1
- package/composables/useLabeledFormElement.ts +3 -4
- package/config/__test__/uiplugins.test.ts +309 -0
- package/config/labels-annotations.js +1 -0
- package/config/product/explorer.js +3 -1
- 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 +7 -7
- package/config/types.js +7 -0
- package/config/uiplugins.js +46 -2
- package/core/__tests__/extension-manager-impl.test.js +437 -0
- package/core/extension-manager-impl.js +21 -25
- 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 +5 -1
- package/core/types.ts +35 -0
- package/detail/provisioning.cattle.io.cluster.vue +9 -6
- package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
- package/dialog/MoveNamespaceDialog.vue +20 -4
- package/dialog/RollbackWorkloadDialog.vue +2 -5
- 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 +100 -3
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
- package/edit/configmap.vue +1 -0
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.helmop.vue +11 -6
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
- package/edit/logging-flow/index.vue +1 -0
- package/edit/logging.banzaicloud.io.output/index.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +1 -1
- package/edit/management.cattle.io.project.vue +1 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
- package/edit/monitoring.coreos.com.route.vue +1 -1
- package/edit/namespace.vue +1 -0
- package/edit/networking.istio.io.destinationrule/index.vue +1 -0
- package/edit/networking.k8s.io.ingress/index.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
- package/edit/node.vue +1 -0
- package/edit/persistentvolume/index.vue +27 -22
- package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
- package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
- package/edit/persistentvolume/plugins/azureFile.vue +15 -14
- package/edit/persistentvolume/plugins/cephfs.vue +15 -14
- package/edit/persistentvolume/plugins/cinder.vue +15 -14
- package/edit/persistentvolume/plugins/csi.vue +18 -16
- package/edit/persistentvolume/plugins/fc.vue +13 -14
- package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
- package/edit/persistentvolume/plugins/flocker.vue +1 -3
- package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
- package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
- package/edit/persistentvolume/plugins/hostPath.vue +40 -39
- package/edit/persistentvolume/plugins/iscsi.vue +13 -14
- package/edit/persistentvolume/plugins/local.vue +1 -3
- package/edit/persistentvolume/plugins/longhorn.vue +23 -22
- package/edit/persistentvolume/plugins/nfs.vue +15 -14
- package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
- package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
- package/edit/persistentvolume/plugins/quobyte.vue +15 -14
- package/edit/persistentvolume/plugins/rbd.vue +15 -14
- package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
- package/edit/persistentvolume/plugins/storageos.vue +15 -14
- package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
- 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 +9 -8
- package/edit/resources.cattle.io.restore.vue +1 -1
- package/edit/secret/index.vue +1 -1
- package/edit/service.vue +1 -0
- package/edit/serviceaccount.vue +1 -0
- package/edit/storage.k8s.io.storageclass/index.vue +1 -0
- package/edit/workload/Job.vue +2 -2
- package/edit/workload/index.vue +2 -1
- package/edit/workload/mixins/workload.js +1 -1
- package/initialize/App.vue +4 -4
- package/initialize/install-plugins.js +19 -5
- package/machine-config/azure.vue +1 -1
- package/machine-config/components/GCEImage.vue +1 -1
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/brand.js +1 -7
- package/mixins/create-edit-view/index.js +5 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +128 -5
- package/models/chart.js +70 -74
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/provisioning.cattle.io.cluster.js +31 -11
- package/package.json +11 -10
- package/pages/auth/login.vue +4 -6
- package/pages/auth/setup.vue +1 -1
- package/pages/auth/verify.vue +3 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
- package/pages/c/_cluster/apps/charts/chart.vue +33 -15
- 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/explorer/index.vue +8 -6
- package/pages/c/_cluster/fleet/index.vue +4 -7
- package/pages/c/_cluster/manager/hostedprovider/index.vue +12 -6
- package/pages/c/_cluster/settings/brand.vue +1 -1
- package/pages/c/_cluster/settings/index.vue +5 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
- package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
- package/pages/c/_cluster/uiplugins/index.vue +126 -184
- 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-client-init.js +3 -0
- package/plugins/dashboard-store/getters.js +19 -2
- package/plugins/dashboard-store/model-loader.js +1 -1
- package/plugins/dashboard-store/mutations.js +23 -2
- package/plugins/dashboard-store/resource-class.js +11 -5
- package/plugins/i18n.js +8 -0
- package/plugins/plugin.js +2 -2
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +506 -0
- package/plugins/steve/steve-class.js +1 -1
- package/plugins/steve/steve-pagination-utils.ts +131 -47
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -42
- package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
- package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
- package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
- package/rancher-components/Pill/types.ts +0 -1
- package/rancher-components/RcDropdown/useDropdownContext.ts +2 -4
- package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
- package/rancher-components/RcIcon/RcIcon.vue +46 -0
- package/rancher-components/RcIcon/index.ts +1 -0
- package/rancher-components/RcIcon/types.ts +160 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +1 -1
- package/rancher-components/utils/status.test.ts +67 -0
- package/rancher-components/utils/status.ts +77 -0
- package/scripts/publish-shell.sh +25 -0
- package/scripts/typegen.sh +1 -0
- package/store/__tests__/catalog.test.ts +1 -1
- package/store/__tests__/type-map.test.ts +164 -2
- package/store/action-menu.js +8 -0
- package/store/auth.js +25 -13
- package/store/catalog.js +6 -0
- package/store/i18n.js +3 -3
- package/store/index.js +8 -6
- package/store/notifications.ts +2 -0
- package/store/prefs.js +6 -7
- package/store/type-map.js +17 -7
- package/store/wm.ts +4 -4
- 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 +54 -3
- package/types/store/__tests__/pagination.types.spec.ts +137 -0
- package/types/store/pagination.types.ts +157 -9
- package/types/vue-shim.d.ts +5 -4
- package/utils/__tests__/provider.test.ts +98 -0
- package/utils/__tests__/router.test.js +238 -0
- package/utils/__tests__/selector-typed.test.ts +263 -0
- package/utils/cluster.js +4 -1
- package/utils/color.js +1 -1
- package/utils/dynamic-content/__tests__/info.test.ts +6 -0
- package/utils/dynamic-content/info.ts +43 -0
- package/utils/favicon.js +4 -4
- package/utils/fleet.ts +8 -1
- package/utils/pagination-utils.ts +2 -2
- package/utils/pagination-wrapper.ts +1 -1
- package/utils/provider.ts +14 -0
- package/utils/router.js +50 -0
- package/utils/selector-typed.ts +6 -2
- package/utils/unit-tests/pagination-utils.spec.ts +8 -8
- package/vue.config.js +3 -3
- package/composables/useExtensionManager.ts +0 -17
- 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/plugins/nuxt-client-init.js +0 -3
- package/types/internal-api/shell/growl.d.ts +0 -25
- package/types/internal-api/shell/slideIn.d.ts +0 -15
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SCHEMA, COUNT, POD, MANAGEMENT, BRAND
|
|
4
|
+
} from '@shell/config/types';
|
|
5
|
+
import { SETTING } from '@shell/config/settings';
|
|
3
6
|
|
|
4
7
|
import { matches } from '@shell/utils/selector';
|
|
5
8
|
import { typeMunge, typeRef, SIMPLE_TYPES } from '@shell/utils/create-yaml';
|
|
@@ -204,6 +207,20 @@ export default {
|
|
|
204
207
|
}
|
|
205
208
|
},
|
|
206
209
|
|
|
210
|
+
brand: (state, getters) => {
|
|
211
|
+
const brand = getters['byId'](MANAGEMENT.SETTING, SETTING.BRAND);
|
|
212
|
+
|
|
213
|
+
if (!brand?.value) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if ([BRAND.CSP, BRAND.FEDERAL, BRAND.RGS].includes(brand.value)) {
|
|
218
|
+
return BRAND.SUSE;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return brand.value;
|
|
222
|
+
},
|
|
223
|
+
|
|
207
224
|
/**
|
|
208
225
|
* Checks a schema for the given path
|
|
209
226
|
*
|
|
@@ -526,7 +543,7 @@ export default {
|
|
|
526
543
|
const store = state.config.namespace;
|
|
527
544
|
const resource = id || context ? { id, context } : null;
|
|
528
545
|
|
|
529
|
-
return paginationUtils.isEnabled({ rootGetters, $
|
|
546
|
+
return paginationUtils.isEnabled({ rootGetters, $extension: rootState.$extension }, { store, resource });
|
|
530
547
|
},
|
|
531
548
|
|
|
532
549
|
/**
|
|
@@ -515,8 +515,29 @@ export default {
|
|
|
515
515
|
cache.generation++;
|
|
516
516
|
|
|
517
517
|
// Update list
|
|
518
|
-
|
|
519
|
-
|
|
518
|
+
// We want to update the old page with the new page
|
|
519
|
+
// We need to keep the cache.list object reference the same
|
|
520
|
+
// We need to keep objects that represent the same resource the same (don't remove the old object and add a new object for the same resource)
|
|
521
|
+
// - this helps anywhere that works with object references (sortable table selection is maintained by object reference)
|
|
522
|
+
|
|
523
|
+
// Create a map of the current references in cache.list
|
|
524
|
+
const currentPageMap = new Map(cache.list.map((i) => [i[keyField], i]));
|
|
525
|
+
|
|
526
|
+
// Create an array containing the new page, but using the same object for resources that exist in old and new page
|
|
527
|
+
const newPage = proxies.map((p) => {
|
|
528
|
+
const existing = currentPageMap.get(p[keyField]);
|
|
529
|
+
|
|
530
|
+
if (existing) {
|
|
531
|
+
replaceResource(existing, p, ctx.getters);
|
|
532
|
+
|
|
533
|
+
return existing;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return p;
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// Replace the old cache.list entries with the new page
|
|
540
|
+
cache.list.splice(0, cache.list.length, ...newPage);
|
|
520
541
|
|
|
521
542
|
// Update Map (remove stale)
|
|
522
543
|
cache.map.forEach((value, key) => {
|
|
@@ -610,7 +610,11 @@ export default class Resource {
|
|
|
610
610
|
}
|
|
611
611
|
|
|
612
612
|
get '$plugin'() {
|
|
613
|
-
return this.$ctx.rootState?.$
|
|
613
|
+
return this.$ctx.rootState?.$extension;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
get '$extension'() {
|
|
617
|
+
return this.$ctx.rootState?.$extension;
|
|
614
618
|
}
|
|
615
619
|
|
|
616
620
|
get customValidationRules() {
|
|
@@ -905,7 +909,7 @@ export default class Resource {
|
|
|
905
909
|
return out;
|
|
906
910
|
}
|
|
907
911
|
|
|
908
|
-
showConfiguration(returnFocusSelector) {
|
|
912
|
+
showConfiguration(returnFocusSelector, defaultTab) {
|
|
909
913
|
const onClose = () => this.$ctx.commit('slideInPanel/close', undefined, { root: true });
|
|
910
914
|
|
|
911
915
|
this.$ctx.commit('slideInPanel/open', {
|
|
@@ -920,7 +924,8 @@ export default class Resource {
|
|
|
920
924
|
'z-index': 101, // We want this to be above the main side menu
|
|
921
925
|
closeOnRouteChange: ['name', 'params', 'query'], // We want to ignore hash changes, tables in extensions can trigger the drawer to close while opening
|
|
922
926
|
triggerFocusTrap: true,
|
|
923
|
-
returnFocusSelector
|
|
927
|
+
returnFocusSelector,
|
|
928
|
+
defaultTab
|
|
924
929
|
}
|
|
925
930
|
}, { root: true });
|
|
926
931
|
}
|
|
@@ -1358,6 +1363,7 @@ export default class Resource {
|
|
|
1358
1363
|
|
|
1359
1364
|
get _detailLocation() {
|
|
1360
1365
|
const schema = this.$getters['schemaFor'](this.type);
|
|
1366
|
+
const isNamespaced = schema?.attributes?.namespaced;
|
|
1361
1367
|
|
|
1362
1368
|
const id = this.id?.replace(/.*\//, '');
|
|
1363
1369
|
|
|
@@ -1367,7 +1373,7 @@ export default class Resource {
|
|
|
1367
1373
|
product: this.$rootGetters['productId'],
|
|
1368
1374
|
cluster: this.$rootGetters['clusterId'],
|
|
1369
1375
|
resource: this.type,
|
|
1370
|
-
namespace: this.metadata?.namespace,
|
|
1376
|
+
namespace: isNamespaced && this.metadata?.namespace ? this.metadata.namespace : undefined,
|
|
1371
1377
|
id,
|
|
1372
1378
|
}
|
|
1373
1379
|
};
|
|
@@ -1776,7 +1782,7 @@ export default class Resource {
|
|
|
1776
1782
|
CustomValidators[validatorName](pathValue, this.$rootGetters, errors, validatorArgs, displayKey, data);
|
|
1777
1783
|
} else if (!isEmpty(validatorName) && !validatorExists) {
|
|
1778
1784
|
// Check if validator is imported from plugin
|
|
1779
|
-
const pluginValidator = this.$rootState.$
|
|
1785
|
+
const pluginValidator = this.$rootState.$extension?.getValidator(validatorName);
|
|
1780
1786
|
|
|
1781
1787
|
if (pluginValidator) {
|
|
1782
1788
|
pluginValidator(pathValue, this.$rootGetters, errors, validatorArgs, displayKey, data);
|
package/plugins/i18n.js
CHANGED
|
@@ -3,6 +3,14 @@ import { escapeHtml } from '../utils/string';
|
|
|
3
3
|
import { watchEffect, ref, h } from 'vue';
|
|
4
4
|
import { useStore } from 'vuex';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('vuex').Store<any>} store
|
|
8
|
+
* @param {string} key
|
|
9
|
+
* @param {Record<string, any>} [args]
|
|
10
|
+
* @param {boolean} [raw]
|
|
11
|
+
* @param {boolean} [escapehtml]
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
6
14
|
export function stringFor(store, key, args, raw = false, escapehtml = true) {
|
|
7
15
|
const translation = store.getters['i18n/t'](key, args);
|
|
8
16
|
|
package/plugins/plugin.js
CHANGED
|
@@ -44,7 +44,7 @@ export default async function(context) {
|
|
|
44
44
|
const res = await allHashSettled(fetches);
|
|
45
45
|
|
|
46
46
|
// Initialize the built-in extensions now - this is now done here so that built-in extensions get the same, correct environment data (version etc)
|
|
47
|
-
context.$
|
|
47
|
+
context.$extension.loadBuiltinExtensions();
|
|
48
48
|
|
|
49
49
|
if (res.plugins?.status === 'rejected') {
|
|
50
50
|
throw new Error(res.reason);
|
|
@@ -60,7 +60,7 @@ export default async function(context) {
|
|
|
60
60
|
const shouldNotLoad = shouldNotLoadPlugin(plugin, { rancherVersion, kubeVersion }, context.store.getters['uiplugins/plugins'] || []); // Error key string or boolean
|
|
61
61
|
|
|
62
62
|
if (!shouldNotLoad) {
|
|
63
|
-
hash[plugin.name] = context.$
|
|
63
|
+
hash[plugin.name] = context.$extension.loadPluginAsync(plugin);
|
|
64
64
|
} else {
|
|
65
65
|
context.store.dispatch('uiplugins/setError', { name: plugin.name, error: shouldNotLoad });
|
|
66
66
|
}
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
|
|
2
|
+
import { PaginationFilterEquality, PaginationFilterField, PaginationParamFilter, PaginationParamProjectOrNamespace } from '@shell/types/store/pagination.types';
|
|
3
|
+
import { NAMESPACE_FILTER_ALL_SYSTEM, NAMESPACE_FILTER_ALL_USER, NAMESPACE_FILTER_P_FULL_PREFIX } from '@shell/utils/namespace-filter';
|
|
4
|
+
import stevePaginationUtils from '../steve-pagination-utils';
|
|
5
|
+
import Schema from '@shell/models/schema';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The `NamespaceProjectFilters` class is a protected class within `steve-pagination-utils.ts`.
|
|
9
|
+
* To test its protected methods, we extend it with a test class that exposes them.
|
|
10
|
+
*/
|
|
11
|
+
class TestNamespaceProjectFilters {
|
|
12
|
+
public handlePrefAndSettingFilter(args: any) {
|
|
13
|
+
return stevePaginationUtils.handlePrefAndSettingFilter(args);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public handleSystemOrUserFilter(args: any) {
|
|
17
|
+
return stevePaginationUtils.handleSystemOrUserFilter(args);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public handleSelectionFilter(neu: string[], isLocalCluster: boolean) {
|
|
21
|
+
return stevePaginationUtils.handleSelectionFilter(neu, isLocalCluster);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public combineNsProjectFilterResults(a: any, b: any) {
|
|
25
|
+
return stevePaginationUtils.combineNsProjectFilterResults(a, b);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public createFiltersFromNamespaceProjectFilterResult(filterResult: any) {
|
|
29
|
+
return stevePaginationUtils.createFiltersFromNamespaceProjectFilterResult(filterResult);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('class: NamespaceProjectFilters', () => {
|
|
34
|
+
const testNamespaceProjectFilters = new TestNamespaceProjectFilters();
|
|
35
|
+
|
|
36
|
+
const normalNs = {
|
|
37
|
+
id: 'normal', name: 'normal', isObscure: false, isSystem: false
|
|
38
|
+
};
|
|
39
|
+
const obscureNs = {
|
|
40
|
+
id: 'obscure', name: 'obscure', isObscure: true, isSystem: false
|
|
41
|
+
};
|
|
42
|
+
const systemNs = {
|
|
43
|
+
id: 'system', name: 'system', isObscure: false, isSystem: true
|
|
44
|
+
};
|
|
45
|
+
const obscureAndSystemNs = {
|
|
46
|
+
id: 'obscure-system', name: 'obscure-system', isObscure: true, isSystem: true
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const allNamespaces = [normalNs, obscureNs, systemNs, obscureAndSystemNs];
|
|
50
|
+
|
|
51
|
+
describe('method: handlePrefAndSettingFilter', () => {
|
|
52
|
+
it('should return no filters if all namespaces are shown', () => {
|
|
53
|
+
const result = testNamespaceProjectFilters.handlePrefAndSettingFilter({
|
|
54
|
+
allNamespaces,
|
|
55
|
+
showReservedRancherNamespaces: true,
|
|
56
|
+
productHidesSystemNamespaces: false,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(result).toStrictEqual({});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should filter obscure namespaces if showReservedRancherNamespaces is false', () => {
|
|
63
|
+
const result = testNamespaceProjectFilters.handlePrefAndSettingFilter({
|
|
64
|
+
allNamespaces,
|
|
65
|
+
showReservedRancherNamespaces: false,
|
|
66
|
+
productHidesSystemNamespaces: false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(result).toStrictEqual({
|
|
70
|
+
obscure: false,
|
|
71
|
+
'obscure-system': false
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should filter system namespaces if productHidesSystemNamespaces is true', () => {
|
|
76
|
+
const result = testNamespaceProjectFilters.handlePrefAndSettingFilter({
|
|
77
|
+
allNamespaces,
|
|
78
|
+
showReservedRancherNamespaces: true,
|
|
79
|
+
productHidesSystemNamespaces: true,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expect(result).toStrictEqual({
|
|
83
|
+
system: false,
|
|
84
|
+
'obscure-system': false
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should filter both obscure and system namespaces when both settings are active', () => {
|
|
89
|
+
const result = testNamespaceProjectFilters.handlePrefAndSettingFilter({
|
|
90
|
+
allNamespaces,
|
|
91
|
+
showReservedRancherNamespaces: false,
|
|
92
|
+
productHidesSystemNamespaces: true,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expect(result).toStrictEqual({
|
|
96
|
+
obscure: false,
|
|
97
|
+
system: false,
|
|
98
|
+
'obscure-system': false
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('method: handleSystemOrUserFilter', () => {
|
|
104
|
+
it('should create an OR filter for system namespaces when isAllSystem is true', () => {
|
|
105
|
+
const result = testNamespaceProjectFilters.handleSystemOrUserFilter({
|
|
106
|
+
allNamespaces,
|
|
107
|
+
isAllSystem: true,
|
|
108
|
+
isAllUser: false,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(result).toStrictEqual({
|
|
112
|
+
system: true,
|
|
113
|
+
'obscure-system': true
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should create AND filters to exclude system namespaces when isAllUser is true', () => {
|
|
118
|
+
const result = testNamespaceProjectFilters.handleSystemOrUserFilter({
|
|
119
|
+
allNamespaces,
|
|
120
|
+
isAllSystem: false,
|
|
121
|
+
isAllUser: true,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect(result).toStrictEqual({
|
|
125
|
+
system: false,
|
|
126
|
+
'obscure-system': false
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('method: combineNsProjectFilterResults', () => {
|
|
132
|
+
it('should merge two results, prioritizing the first', () => {
|
|
133
|
+
const a = { ns1: true, ns2: false };
|
|
134
|
+
const b = { ns2: true, ns3: true };
|
|
135
|
+
const result = testNamespaceProjectFilters.combineNsProjectFilterResults(a, b);
|
|
136
|
+
|
|
137
|
+
expect(result).toStrictEqual({
|
|
138
|
+
ns1: true,
|
|
139
|
+
ns2: false, // kept from a
|
|
140
|
+
ns3: true // added from b
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('method: createFiltersFromNamespaceProjectFilterResult', () => {
|
|
146
|
+
it('should create IN filter if there are included namespaces', () => {
|
|
147
|
+
const input = {
|
|
148
|
+
ns1: true, ns2: true, ns3: false
|
|
149
|
+
};
|
|
150
|
+
const result = testNamespaceProjectFilters.createFiltersFromNamespaceProjectFilterResult(input);
|
|
151
|
+
|
|
152
|
+
expect(result).toHaveLength(1);
|
|
153
|
+
expect(result[0].fields[0]).toMatchObject({
|
|
154
|
+
field: 'metadata.namespace',
|
|
155
|
+
equality: PaginationFilterEquality.IN,
|
|
156
|
+
value: 'ns1,ns2'
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should create NOT_IN filter if there are only excluded namespaces', () => {
|
|
161
|
+
const input = { ns1: false, ns2: false };
|
|
162
|
+
const result = testNamespaceProjectFilters.createFiltersFromNamespaceProjectFilterResult(input);
|
|
163
|
+
|
|
164
|
+
expect(result).toHaveLength(1);
|
|
165
|
+
expect(result[0].fields[0]).toMatchObject({
|
|
166
|
+
field: 'metadata.namespace',
|
|
167
|
+
equality: PaginationFilterEquality.NOT_IN,
|
|
168
|
+
value: 'ns1,ns2'
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should return empty array if input is empty', () => {
|
|
173
|
+
const input = {};
|
|
174
|
+
const result = testNamespaceProjectFilters.createFiltersFromNamespaceProjectFilterResult(input);
|
|
175
|
+
|
|
176
|
+
expect(result).toHaveLength(0);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('method: handleSelectionFilter', () => {
|
|
181
|
+
const selection = ['ns-1', `${ NAMESPACE_FILTER_P_FULL_PREFIX }p-1`];
|
|
182
|
+
|
|
183
|
+
it('should create projectsOrNamespaces filter for a selection in a non-local cluster', () => {
|
|
184
|
+
const result = testNamespaceProjectFilters.handleSelectionFilter(selection, false);
|
|
185
|
+
|
|
186
|
+
expect(result.projectsOrNamespaces).toHaveLength(1);
|
|
187
|
+
const pnsFilter = result.projectsOrNamespaces[0] as PaginationParamProjectOrNamespace;
|
|
188
|
+
|
|
189
|
+
expect(pnsFilter.param).toBe('projectsornamespaces');
|
|
190
|
+
expect(pnsFilter.fields).toHaveLength(2);
|
|
191
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'ns-1' }));
|
|
192
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'p-1' }));
|
|
193
|
+
expect(result.filters).toStrictEqual([]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should create projectsOrNamespaces and an exclusion filter for a selection in a local cluster', () => {
|
|
197
|
+
const result = testNamespaceProjectFilters.handleSelectionFilter(selection, true);
|
|
198
|
+
|
|
199
|
+
// projectsOrNamespaces part
|
|
200
|
+
expect(result.projectsOrNamespaces).toHaveLength(1);
|
|
201
|
+
const pnsFilter = result.projectsOrNamespaces[0] as PaginationParamProjectOrNamespace;
|
|
202
|
+
|
|
203
|
+
expect(pnsFilter.param).toBe('projectsornamespaces');
|
|
204
|
+
expect(pnsFilter.fields).toHaveLength(2);
|
|
205
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'ns-1' }));
|
|
206
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'p-1' }));
|
|
207
|
+
|
|
208
|
+
// filters part
|
|
209
|
+
expect(result.filters).toHaveLength(1);
|
|
210
|
+
const filter = result.filters[0] as PaginationParamFilter;
|
|
211
|
+
|
|
212
|
+
expect(filter.fields).toHaveLength(1);
|
|
213
|
+
expect(filter.fields).toContainEqual(expect.objectContaining({
|
|
214
|
+
field: 'metadata.namespace',
|
|
215
|
+
equality: '!=',
|
|
216
|
+
value: 'p-1',
|
|
217
|
+
}));
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should handle selections with only namespaces in a local cluster', () => {
|
|
221
|
+
const nsSelection = ['ns-1', 'ns-2'];
|
|
222
|
+
const result = testNamespaceProjectFilters.handleSelectionFilter(nsSelection, true);
|
|
223
|
+
|
|
224
|
+
expect(result.projectsOrNamespaces).toHaveLength(1);
|
|
225
|
+
const pnsFilter = result.projectsOrNamespaces[0] as PaginationParamProjectOrNamespace;
|
|
226
|
+
|
|
227
|
+
expect(pnsFilter.fields).toHaveLength(2);
|
|
228
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'ns-1' }));
|
|
229
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'ns-2' }));
|
|
230
|
+
expect(result.filters).toStrictEqual([]);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should handle selections with only projects in a local cluster', () => {
|
|
234
|
+
const projectSelection = [`${ NAMESPACE_FILTER_P_FULL_PREFIX }p-1`, `${ NAMESPACE_FILTER_P_FULL_PREFIX }p-2`];
|
|
235
|
+
const result = testNamespaceProjectFilters.handleSelectionFilter(projectSelection, true);
|
|
236
|
+
|
|
237
|
+
expect(result.projectsOrNamespaces).toHaveLength(1);
|
|
238
|
+
const pnsFilter = result.projectsOrNamespaces[0] as PaginationParamProjectOrNamespace;
|
|
239
|
+
|
|
240
|
+
expect(pnsFilter.fields).toHaveLength(2);
|
|
241
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'p-1' }));
|
|
242
|
+
expect(pnsFilter.fields).toContainEqual(expect.objectContaining({ value: 'p-2' }));
|
|
243
|
+
|
|
244
|
+
expect(result.filters).toHaveLength(2);
|
|
245
|
+
expect(result.filters).toContainEqual(expect.objectContaining({
|
|
246
|
+
fields: [expect.objectContaining({
|
|
247
|
+
field: 'metadata.namespace',
|
|
248
|
+
equality: '!=',
|
|
249
|
+
value: 'p-1'
|
|
250
|
+
})],
|
|
251
|
+
}));
|
|
252
|
+
expect(result.filters).toContainEqual(expect.objectContaining({
|
|
253
|
+
fields: [expect.objectContaining({
|
|
254
|
+
field: 'metadata.namespace',
|
|
255
|
+
equality: '!=',
|
|
256
|
+
value: 'p-2'
|
|
257
|
+
})],
|
|
258
|
+
}));
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
class TestStevePaginationUtils {
|
|
264
|
+
public convertPaginationParams(args: any) {
|
|
265
|
+
return stevePaginationUtils.convertPaginationParams(args);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
describe('class StevePaginationUtils', () => {
|
|
270
|
+
const testStevePaginationUtils = new TestStevePaginationUtils();
|
|
271
|
+
const schema = { id: 'pod' } as unknown as Schema;
|
|
272
|
+
|
|
273
|
+
describe('method: createParamsFromNsFilter', () => {
|
|
274
|
+
const normalNs = {
|
|
275
|
+
id: 'normal', name: 'normal', isObscure: false, isSystem: false
|
|
276
|
+
} as any;
|
|
277
|
+
const obscureNs = {
|
|
278
|
+
id: 'obscure', name: 'obscure', isObscure: true, isSystem: false
|
|
279
|
+
} as any;
|
|
280
|
+
const systemNs = {
|
|
281
|
+
id: 'system', name: 'system', isObscure: false, isSystem: true
|
|
282
|
+
} as any;
|
|
283
|
+
const allNamespaces = [normalNs, obscureNs, systemNs];
|
|
284
|
+
|
|
285
|
+
it('should return empty filters if all namespaces requested and settings allow all', () => {
|
|
286
|
+
const result = stevePaginationUtils.createParamsFromNsFilter({
|
|
287
|
+
allNamespaces,
|
|
288
|
+
selection: [],
|
|
289
|
+
isAllNamespaces: true,
|
|
290
|
+
isLocalCluster: false,
|
|
291
|
+
showReservedRancherNamespaces: true,
|
|
292
|
+
productHidesSystemNamespaces: false,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(result.projectsOrNamespaces).toHaveLength(0);
|
|
296
|
+
expect(result.filters).toHaveLength(0);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should filter obscure namespaces if showReservedRancherNamespaces is false', () => {
|
|
300
|
+
const result = stevePaginationUtils.createParamsFromNsFilter({
|
|
301
|
+
allNamespaces,
|
|
302
|
+
selection: [],
|
|
303
|
+
isAllNamespaces: true,
|
|
304
|
+
isLocalCluster: false,
|
|
305
|
+
showReservedRancherNamespaces: false,
|
|
306
|
+
productHidesSystemNamespaces: false,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
expect(result.filters).toHaveLength(1);
|
|
310
|
+
expect(result.filters[0].fields[0]).toMatchObject({
|
|
311
|
+
field: 'metadata.namespace',
|
|
312
|
+
equality: PaginationFilterEquality.NOT_IN,
|
|
313
|
+
value: 'obscure'
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should filter system namespaces if productHidesSystemNamespaces is true', () => {
|
|
318
|
+
const result = stevePaginationUtils.createParamsFromNsFilter({
|
|
319
|
+
allNamespaces,
|
|
320
|
+
selection: [],
|
|
321
|
+
isAllNamespaces: true,
|
|
322
|
+
isLocalCluster: false,
|
|
323
|
+
showReservedRancherNamespaces: true,
|
|
324
|
+
productHidesSystemNamespaces: true,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
expect(result.filters).toHaveLength(1);
|
|
328
|
+
expect(result.filters[0].fields[0]).toMatchObject({
|
|
329
|
+
field: 'metadata.namespace',
|
|
330
|
+
equality: PaginationFilterEquality.NOT_IN,
|
|
331
|
+
value: 'system'
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should filter both obscure and system namespaces when both settings are active', () => {
|
|
336
|
+
const result = stevePaginationUtils.createParamsFromNsFilter({
|
|
337
|
+
allNamespaces,
|
|
338
|
+
selection: [],
|
|
339
|
+
isAllNamespaces: true,
|
|
340
|
+
isLocalCluster: false,
|
|
341
|
+
showReservedRancherNamespaces: false,
|
|
342
|
+
productHidesSystemNamespaces: true,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
expect(result.filters).toHaveLength(1);
|
|
346
|
+
expect(result.filters[0].fields[0]).toMatchObject({
|
|
347
|
+
field: 'metadata.namespace',
|
|
348
|
+
equality: PaginationFilterEquality.NOT_IN,
|
|
349
|
+
value: 'obscure,system'
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('should handle ALL_SYSTEM selection', () => {
|
|
354
|
+
const result = stevePaginationUtils.createParamsFromNsFilter({
|
|
355
|
+
allNamespaces,
|
|
356
|
+
selection: [NAMESPACE_FILTER_ALL_SYSTEM],
|
|
357
|
+
isAllNamespaces: false,
|
|
358
|
+
isLocalCluster: false,
|
|
359
|
+
showReservedRancherNamespaces: true,
|
|
360
|
+
productHidesSystemNamespaces: false,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(result.filters).toHaveLength(1);
|
|
364
|
+
expect(result.filters[0].fields[0]).toMatchObject({
|
|
365
|
+
field: 'metadata.namespace',
|
|
366
|
+
equality: PaginationFilterEquality.IN,
|
|
367
|
+
value: 'system'
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should handle ALL_USER selection', () => {
|
|
372
|
+
const result = stevePaginationUtils.createParamsFromNsFilter({
|
|
373
|
+
allNamespaces,
|
|
374
|
+
selection: [NAMESPACE_FILTER_ALL_USER],
|
|
375
|
+
isAllNamespaces: false,
|
|
376
|
+
isLocalCluster: false,
|
|
377
|
+
showReservedRancherNamespaces: true,
|
|
378
|
+
productHidesSystemNamespaces: false,
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
expect(result.filters).toHaveLength(1);
|
|
382
|
+
expect(result.filters[0].fields[0]).toMatchObject({
|
|
383
|
+
field: 'metadata.namespace',
|
|
384
|
+
equality: PaginationFilterEquality.NOT_IN,
|
|
385
|
+
value: 'system'
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should handle specific project/namespace selection', () => {
|
|
390
|
+
const selection = ['ns-1'];
|
|
391
|
+
const result = stevePaginationUtils.createParamsFromNsFilter({
|
|
392
|
+
allNamespaces,
|
|
393
|
+
selection,
|
|
394
|
+
isAllNamespaces: false,
|
|
395
|
+
isLocalCluster: false,
|
|
396
|
+
showReservedRancherNamespaces: true,
|
|
397
|
+
productHidesSystemNamespaces: false,
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
expect(result.projectsOrNamespaces).toHaveLength(1);
|
|
401
|
+
expect(result.projectsOrNamespaces[0].fields[0].value).toBe('ns-1');
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe('method: convertPaginationParams', () => {
|
|
406
|
+
it('should return an empty string for no filters', () => {
|
|
407
|
+
const result = testStevePaginationUtils.convertPaginationParams({ schema, filters: [] });
|
|
408
|
+
|
|
409
|
+
expect(result).toBe('');
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should handle a single filter with a single field', () => {
|
|
413
|
+
const filters = [
|
|
414
|
+
new PaginationParamFilter({ fields: [new PaginationFilterField({ field: 'metadata.name', value: 'test' })] }),
|
|
415
|
+
];
|
|
416
|
+
const result = testStevePaginationUtils.convertPaginationParams({ schema, filters });
|
|
417
|
+
|
|
418
|
+
expect(result).toBe('filter=metadata.name=test');
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should handle a single filter with a single field with encoded char', () => {
|
|
422
|
+
const filters = [
|
|
423
|
+
new PaginationParamFilter({ fields: [new PaginationFilterField({ field: 'metadata.name', value: 'te/st' })] }),
|
|
424
|
+
];
|
|
425
|
+
const result = testStevePaginationUtils.convertPaginationParams({ schema, filters });
|
|
426
|
+
|
|
427
|
+
expect(result).toBe('filter=metadata.name="te%2Fst"');
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should handle a single filter with multiple fields (OR)', () => {
|
|
431
|
+
const filters = [
|
|
432
|
+
new PaginationParamFilter({
|
|
433
|
+
fields: [
|
|
434
|
+
new PaginationFilterField({ field: 'metadata.name', value: 'test1' }),
|
|
435
|
+
new PaginationFilterField({ field: 'metadata.namespace', value: 'ns1' }),
|
|
436
|
+
],
|
|
437
|
+
}),
|
|
438
|
+
];
|
|
439
|
+
const result = testStevePaginationUtils.convertPaginationParams({ schema, filters });
|
|
440
|
+
|
|
441
|
+
expect(result).toBe('filter=metadata.name=test1,metadata.namespace=ns1');
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('should handle multiple filters (AND)', () => {
|
|
445
|
+
const filters = [
|
|
446
|
+
new PaginationParamFilter({ fields: [new PaginationFilterField({ field: 'metadata.name', value: 'test1' })] }),
|
|
447
|
+
new PaginationParamFilter({ fields: [new PaginationFilterField({ field: 'metadata.namespace', value: 'ns1' })] }),
|
|
448
|
+
];
|
|
449
|
+
const result = testStevePaginationUtils.convertPaginationParams({ schema, filters });
|
|
450
|
+
|
|
451
|
+
expect(result).toBe('filter=metadata.name=test1&filter=metadata.namespace=ns1');
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('should handle different equality operators', () => {
|
|
455
|
+
const filters = [
|
|
456
|
+
new PaginationParamFilter({
|
|
457
|
+
fields: [
|
|
458
|
+
new PaginationFilterField({
|
|
459
|
+
field: 'spec.containers.image',
|
|
460
|
+
value: 'nginx',
|
|
461
|
+
equality: PaginationFilterEquality.CONTAINS,
|
|
462
|
+
}),
|
|
463
|
+
],
|
|
464
|
+
}),
|
|
465
|
+
new PaginationParamFilter({
|
|
466
|
+
fields: [
|
|
467
|
+
new PaginationFilterField({
|
|
468
|
+
field: 'metadata.name',
|
|
469
|
+
value: 'test',
|
|
470
|
+
equality: PaginationFilterEquality.NOT_EQUALS,
|
|
471
|
+
}),
|
|
472
|
+
],
|
|
473
|
+
}),
|
|
474
|
+
];
|
|
475
|
+
const result = testStevePaginationUtils.convertPaginationParams({ schema, filters });
|
|
476
|
+
|
|
477
|
+
expect(result).toBe('filter=spec.containers.image~nginx&filter=metadata.name!=test');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('should handle IN and NOT_IN operators', () => {
|
|
481
|
+
const filters = [
|
|
482
|
+
new PaginationParamFilter({
|
|
483
|
+
fields: [
|
|
484
|
+
new PaginationFilterField({
|
|
485
|
+
field: 'metadata.name',
|
|
486
|
+
value: 'test1,test2',
|
|
487
|
+
equality: PaginationFilterEquality.IN,
|
|
488
|
+
}),
|
|
489
|
+
],
|
|
490
|
+
}),
|
|
491
|
+
new PaginationParamFilter({
|
|
492
|
+
fields: [
|
|
493
|
+
new PaginationFilterField({
|
|
494
|
+
field: 'metadata.namespace',
|
|
495
|
+
value: 'ns1,ns2',
|
|
496
|
+
equality: PaginationFilterEquality.NOT_IN,
|
|
497
|
+
}),
|
|
498
|
+
],
|
|
499
|
+
}),
|
|
500
|
+
];
|
|
501
|
+
const result = testStevePaginationUtils.convertPaginationParams({ schema, filters });
|
|
502
|
+
|
|
503
|
+
expect(result).toBe('filter=metadata.name IN (test1,test2)&filter=metadata.namespace NOTIN (ns1,ns2)');
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
});
|
|
@@ -47,7 +47,7 @@ export default class SteveModel extends HybridModel {
|
|
|
47
47
|
* Get all model extensions for this model
|
|
48
48
|
*/
|
|
49
49
|
get modelExtensions() {
|
|
50
|
-
return this.$
|
|
50
|
+
return this.$extension.getDynamic(EXT_IDS.MODEL_EXTENSION, this.type) || [];
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
cleanForSave(data, forNew) {
|