@rancher/shell 3.0.8-rc.1 → 3.0.8-rc.12
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/assets/brand/suse/banner.svg +1 -0
- package/assets/brand/suse/dark/banner.svg +1 -0
- package/assets/brand/suse/dark/login-landscape.svg +1 -0
- package/assets/brand/suse/dark/rancher-logo.svg +1 -1
- package/assets/brand/suse/favicon.png +0 -0
- package/assets/brand/suse/login-landscape.svg +1 -0
- package/assets/brand/suse/metadata.json +11 -1
- package/assets/brand/suse/rancher-logo.svg +1 -1
- package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
- package/assets/images/content/README.md +5 -0
- package/assets/images/content/cloud-native.svg +84 -0
- package/assets/images/content/dark/cloud-native.svg +21 -0
- package/assets/images/content/dark/shield.svg +59 -0
- package/assets/images/content/dark/suse.svg +10 -0
- package/assets/images/content/shield.svg +59 -0
- package/assets/images/content/suse.svg +10 -0
- package/assets/styles/base/_typography.scss +1 -0
- package/assets/styles/fonts/_fontstack.scss +53 -1
- package/assets/styles/global/_cards.scss +0 -3
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_dark.scss +1 -1
- package/assets/styles/themes/_light.scss +1 -1
- package/assets/styles/themes/_modern.scss +11 -3
- package/assets/styles/themes/_suse.scss +116 -24
- package/assets/translations/en-us.yaml +94 -10
- package/components/AutoscalerCard.vue +113 -0
- package/components/AutoscalerTab.vue +94 -0
- package/components/BackLink.vue +8 -0
- package/components/BannerGraphic.vue +36 -21
- package/components/BrandImage.vue +17 -6
- package/components/ClusterIconMenu.vue +1 -1
- package/components/ClusterProviderIcon.vue +1 -1
- package/components/Cron/CronExpressionEditor.vue +1 -1
- package/components/Cron/CronExpressionEditorModal.vue +1 -1
- package/components/Drawer/Chrome.vue +2 -6
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
- package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
- package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
- package/components/Drawer/types.ts +3 -0
- package/components/DynamicContent/DynamicContentBanner.vue +102 -0
- package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
- package/components/DynamicContent/DynamicContentIcon.vue +132 -0
- package/components/DynamicContent/DynamicContentPanel.vue +112 -0
- package/components/DynamicContent/content.ts +78 -0
- package/components/EmberPage.vue +1 -1
- package/components/IconOrSvg.vue +2 -2
- package/components/PaginatedResourceTable.vue +2 -6
- package/components/PopoverCard.vue +192 -0
- package/components/Questions/__tests__/index.test.ts +159 -0
- package/components/Resource/Detail/CopyToClipboard.vue +4 -1
- package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
- package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
- package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
- package/components/Resource/Detail/Metadata/composables.ts +9 -9
- package/components/Resource/Detail/Metadata/index.vue +3 -3
- package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
- package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
- package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/composables.ts +2 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/Resource/Detail/composables.ts +12 -0
- package/components/ResourceDetail/Masthead/latest.vue +29 -0
- package/components/ResourceDetail/index.vue +4 -1
- package/components/ResourceList/Masthead.vue +1 -1
- package/components/ResourceTable.vue +1 -1
- package/components/SortableTable/index.vue +2 -1
- package/components/Tabbed/__tests__/index.test.ts +86 -0
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- package/components/__tests__/AutoscalerCard.test.ts +154 -0
- package/components/__tests__/AutoscalerTab.test.ts +125 -0
- package/components/__tests__/PopoverCard.test.ts +204 -0
- package/components/auth/SelectPrincipal.vue +24 -6
- package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
- package/components/auth/login/ldap.vue +3 -3
- package/components/form/NodeScheduling.vue +2 -2
- package/components/formatter/Autoscaler.vue +97 -0
- package/components/formatter/InternalExternalIP.vue +198 -24
- package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
- package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
- package/components/google/util/__tests__/formatter.test.ts +47 -0
- package/components/google/util/formatter.ts +5 -2
- package/components/nav/Group.vue +21 -5
- package/components/nav/Header.vue +37 -17
- package/components/nav/NamespaceFilter.vue +13 -1
- package/components/nav/NotificationCenter/index.vue +2 -1
- package/components/nav/TopLevelMenu.helper.ts +16 -6
- package/components/nav/TopLevelMenu.vue +4 -2
- package/components/nav/Type.vue +8 -3
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/nav/__tests__/Type.test.ts +59 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/components/templates/plain.vue +30 -4
- package/components/templates/standalone.vue +1 -1
- package/composables/useI18n.ts +10 -1
- package/composables/useInterval.ts +15 -0
- package/config/__test__/uiplugins.test.ts +309 -0
- package/config/labels-annotations.js +9 -1
- package/config/product/explorer.js +3 -1
- package/config/product/manager.js +20 -9
- package/config/router/navigation-guards/clusters.js +3 -3
- package/config/router/navigation-guards/products.js +1 -1
- package/config/router/routes.js +10 -2
- package/config/settings.ts +2 -1
- package/config/store.js +4 -2
- package/config/table-headers.js +8 -0
- package/config/types.js +9 -0
- package/config/uiplugins.js +46 -2
- package/config/version.js +1 -1
- package/core/__test__/extension-manager-impl.test.js +236 -0
- package/core/extension-manager-impl.js +21 -4
- package/core/plugin-helpers.ts +4 -2
- package/core/plugins-loader.js +2 -2
- package/core/types-provisioning.ts +8 -1
- package/detail/pod.vue +1 -0
- package/detail/provisioning.cattle.io.cluster.vue +19 -7
- package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
- package/dialog/RollbackWorkloadDialog.vue +2 -5
- package/dialog/SearchDialog.vue +1 -0
- package/directives/ui-context.ts +103 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +2 -2
- package/edit/auth/__tests__/oidc.test.ts +26 -0
- package/edit/auth/github.vue +5 -0
- package/edit/auth/oidc.vue +5 -1
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
- package/edit/cloudcredential.vue +1 -1
- package/edit/configmap.vue +1 -0
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.gitrepo.vue +0 -10
- package/edit/fleet.cattle.io.helmop.vue +6 -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/CustomCommand.vue +32 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
- package/edit/provisioning.cattle.io.cluster/index.vue +28 -18
- package/edit/provisioning.cattle.io.cluster/rke2.vue +50 -16
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
- package/edit/secret/index.vue +1 -1
- package/edit/service.vue +9 -4
- package/edit/serviceaccount.vue +1 -0
- package/edit/storage.k8s.io.storageclass/index.vue +1 -0
- package/edit/workload/index.vue +2 -1
- package/edit/workload/mixins/workload.js +1 -1
- package/initialize/App.vue +4 -4
- package/initialize/install-directives.js +2 -0
- package/initialize/install-plugins.js +19 -2
- package/list/provisioning.cattle.io.cluster.vue +15 -2
- package/machine-config/amazonec2.vue +42 -135
- package/machine-config/components/EC2Networking.vue +490 -0
- package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
- package/machine-config/digitalocean.vue +11 -0
- package/machine-config/google.vue +1 -1
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/__tests__/chart.test.ts +21 -0
- package/mixins/brand.js +1 -7
- package/mixins/chart.js +7 -1
- package/mixins/create-edit-view/index.js +5 -0
- package/models/__tests__/chart.test.ts +33 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
- package/models/chart.js +25 -13
- package/models/cluster/node.js +13 -6
- package/models/cluster.x-k8s.io.machine.js +10 -20
- package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +249 -33
- package/package.json +8 -7
- package/pages/auth/login.vue +41 -5
- 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 +11 -13
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/index.vue +8 -6
- package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
- package/pages/c/_cluster/settings/brand.vue +1 -1
- 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/pages/home.vue +14 -4
- package/pkg/auto-import.js +3 -3
- package/pkg/dynamic-importer.lib.js +5 -1
- package/pkg/import.js +1 -1
- 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/resource-class.js +10 -6
- package/plugins/dynamic-content.js +13 -0
- package/plugins/i18n.js +8 -0
- package/plugins/plugin.js +2 -2
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
- package/plugins/steve/steve-class.js +1 -1
- package/plugins/steve/steve-pagination-utils.ts +39 -20
- package/plugins/steve/subscribe.js +17 -9
- package/plugins/subscribe-events.ts +4 -2
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
- 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/RcDropdownItem.vue +1 -0
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- 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/utils/status.test.ts +67 -0
- package/rancher-components/utils/status.ts +77 -0
- package/scripts/typegen.sh +1 -0
- package/store/__tests__/catalog.test.ts +1 -1
- package/store/action-menu.js +8 -0
- package/store/auth.js +4 -4
- package/store/catalog.js +6 -0
- package/store/features.js +1 -0
- package/store/i18n.js +3 -3
- package/store/index.js +40 -19
- package/store/notifications.ts +51 -4
- package/store/plugins.js +7 -3
- package/store/prefs.js +6 -6
- package/store/type-map.js +7 -7
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/notifications/index.ts +27 -3
- package/types/shell/index.d.ts +80 -4
- package/types/store/__tests__/pagination.types.spec.ts +137 -0
- package/types/store/pagination.types.ts +157 -9
- package/types/store/subscribe-events.types.ts +8 -1
- package/types/store/subscribe.types.ts +1 -0
- package/types/window-manager.ts +24 -0
- package/utils/__tests__/object.test.ts +19 -0
- package/utils/__tests__/provider.test.ts +98 -0
- package/utils/__tests__/selector-typed.test.ts +263 -0
- package/utils/__tests__/version.test.ts +19 -1
- package/utils/autoscaler-utils.ts +7 -0
- package/utils/back-off.ts +3 -3
- package/utils/brand.ts +29 -0
- package/utils/chart.js +18 -0
- package/utils/color.js +1 -1
- package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
- package/utils/dynamic-content/__tests__/info.test.ts +21 -9
- package/utils/dynamic-content/announcement.ts +142 -0
- package/utils/dynamic-content/example.json +40 -0
- package/utils/dynamic-content/index.ts +6 -2
- package/utils/dynamic-content/info.ts +44 -2
- package/utils/dynamic-content/new-release.ts +1 -1
- package/utils/dynamic-content/notification-handler.ts +48 -0
- package/utils/dynamic-content/types.d.ts +53 -1
- package/utils/dynamic-importer.js +2 -2
- package/utils/favicon.js +4 -4
- package/utils/object.js +20 -2
- package/utils/pagination-utils.ts +2 -2
- package/utils/pagination-wrapper.ts +13 -9
- package/utils/provider.ts +14 -0
- package/utils/scroll.js +7 -0
- package/utils/selector-typed.ts +6 -2
- package/utils/settings.ts +15 -0
- package/utils/unit-tests/pagination-utils.spec.ts +8 -8
- package/utils/validators/machine-pool.ts +13 -3
- package/utils/version.js +15 -0
- package/vue.config.js +3 -3
- package/assets/images/icons/document.svg +0 -3
- package/plugins/nuxt-client-init.js +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -13,6 +13,8 @@ import { KubeLabelSelector } from '@shell/types/kube/kube-api';
|
|
|
13
13
|
// The two important / complex params are currently
|
|
14
14
|
// - `filter` https://github.com/rancher/steve?tab=readme-ov-file#filter
|
|
15
15
|
// - represented by `PaginationParamFilter extends PaginationParam`
|
|
16
|
+
// - a filter has filter fields which are made up of a field name, equality and value/s
|
|
17
|
+
// - filter=<field><equality><value>
|
|
16
18
|
// - Examples
|
|
17
19
|
// - filter=metadata.name=123
|
|
18
20
|
// - filter=metadata.name=123,metadata.name=456 (name is 123 OR 456)
|
|
@@ -45,14 +47,111 @@ export interface PaginationSort {
|
|
|
45
47
|
asc: boolean
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Equalities that can be used with a `filter` query param
|
|
52
|
+
*
|
|
53
|
+
* filter=<field><equality><values>
|
|
54
|
+
*
|
|
55
|
+
* For example
|
|
56
|
+
* - filter=a=b
|
|
57
|
+
* - filter=a!=b
|
|
58
|
+
* - filter=a NOT IN (b,c,d)
|
|
59
|
+
*/
|
|
60
|
+
export const enum PaginationFilterEquality {
|
|
61
|
+
/**
|
|
62
|
+
* Field is in a collection of values
|
|
63
|
+
*/
|
|
64
|
+
IN = ' IN ', // eslint-disable-line no-unused-vars
|
|
65
|
+
/**
|
|
66
|
+
* Field is not in a collection of values
|
|
67
|
+
*/
|
|
68
|
+
NOT_IN = ' NOTIN ', // eslint-disable-line no-unused-vars
|
|
69
|
+
/**
|
|
70
|
+
* Field matches a value
|
|
71
|
+
*/
|
|
72
|
+
EQUALS= '=', // eslint-disable-line no-unused-vars
|
|
73
|
+
/**
|
|
74
|
+
* Field does not match a value
|
|
75
|
+
*/
|
|
76
|
+
NOT_EQUALS= '!=', // eslint-disable-line no-unused-vars
|
|
77
|
+
/**
|
|
78
|
+
* Unknown
|
|
79
|
+
*/
|
|
80
|
+
STRICT_EQUALS = '==', // eslint-disable-line no-unused-vars
|
|
81
|
+
/**
|
|
82
|
+
* Field must partially match a value
|
|
83
|
+
*/
|
|
84
|
+
CONTAINS= '~', // eslint-disable-line no-unused-vars
|
|
85
|
+
/**
|
|
86
|
+
* Field must not partially match a value
|
|
87
|
+
*/
|
|
88
|
+
NOT_CONTAINS= '!~', // eslint-disable-line no-unused-vars
|
|
89
|
+
/**
|
|
90
|
+
* Field must be greater than a value
|
|
91
|
+
*/
|
|
92
|
+
GREATER_THAN= 'gt', // eslint-disable-line no-unused-vars
|
|
93
|
+
/**
|
|
94
|
+
* Field must be less than a value
|
|
95
|
+
*/
|
|
96
|
+
LESS_THAN= 'lt', // eslint-disable-line no-unused-vars
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Ctor args for a @PaginationFilterField
|
|
101
|
+
*/
|
|
102
|
+
type FilterFieldCtorArgs = {
|
|
103
|
+
/**
|
|
104
|
+
* Name of field within the object to filter by for example the x of x=y
|
|
105
|
+
*
|
|
106
|
+
* This can be optional for some (projectsornamespaces)
|
|
107
|
+
*/
|
|
108
|
+
field?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Value of field within the object to filter by for example the y of x=y
|
|
111
|
+
*
|
|
112
|
+
* This can be empty if `exists` is true
|
|
113
|
+
*/
|
|
114
|
+
value?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Equality field within the object to filter by for example the `=` or `!=` of x=y
|
|
117
|
+
*
|
|
118
|
+
* @deprecated Please use `equality` instead of equals and exact
|
|
119
|
+
*/
|
|
120
|
+
equals?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Match the field exactly. False for partial matches
|
|
123
|
+
*
|
|
124
|
+
* Value: pod1
|
|
125
|
+
* Exact: true. "p" no, "pod", no, "pod1" yes
|
|
126
|
+
* Exact: false. "p" yes, "pod", yes, "pod1" yes
|
|
127
|
+
*
|
|
128
|
+
* @deprecated Please use `equality` instead of equals and exact
|
|
129
|
+
*/
|
|
130
|
+
exact?: boolean,
|
|
131
|
+
/**
|
|
132
|
+
* Check if the field/property exists, regardless of value
|
|
133
|
+
*
|
|
134
|
+
* If this is false it does not flip the expectation, just doesn't add the field
|
|
135
|
+
*/
|
|
136
|
+
exists?: boolean,
|
|
137
|
+
/**
|
|
138
|
+
* Equality symbol used to compare the field with the value
|
|
139
|
+
*/
|
|
140
|
+
equality?: PaginationFilterEquality
|
|
141
|
+
}
|
|
142
|
+
|
|
48
143
|
/**
|
|
49
144
|
* Filter the pagination result by these specific fields
|
|
50
145
|
*
|
|
146
|
+
* In format of <field><equality><value>
|
|
147
|
+
*
|
|
51
148
|
* For example
|
|
52
149
|
*
|
|
53
150
|
* - metadata.name=test
|
|
54
151
|
* - metadata.namespace!=system
|
|
55
152
|
*
|
|
153
|
+
* These are sub items for @PaginationParam, for example filter=<PaginationFilterField>
|
|
154
|
+
*
|
|
56
155
|
* For more information regarding the API see https://github.com/rancher/steve?tab=readme-ov-file#query-parameters
|
|
57
156
|
*/
|
|
58
157
|
export class PaginationFilterField {
|
|
@@ -70,6 +169,8 @@ export class PaginationFilterField {
|
|
|
70
169
|
value?: string;
|
|
71
170
|
/**
|
|
72
171
|
* Equality field within the object to filter by for example the `=` or `!=` of x=y
|
|
172
|
+
*
|
|
173
|
+
* @deprecated Please use `equality` instead of equals and exact
|
|
73
174
|
*/
|
|
74
175
|
equals?: boolean;
|
|
75
176
|
/**
|
|
@@ -78,9 +179,14 @@ export class PaginationFilterField {
|
|
|
78
179
|
* Value: pod1
|
|
79
180
|
* Exact: true. "p" no, "pod", no, "pod1" yes
|
|
80
181
|
* Exact: false. "p" yes, "pod", yes, "pod1" yes
|
|
182
|
+
*
|
|
183
|
+
* @deprecated Please use `equality` instead of equals and exact
|
|
81
184
|
*/
|
|
82
185
|
exact?: boolean;
|
|
83
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Equality symbol used to compare the field with the value
|
|
188
|
+
*/
|
|
189
|
+
equality?: PaginationFilterEquality;
|
|
84
190
|
/**
|
|
85
191
|
* Check if the field/property exists, regardless of value
|
|
86
192
|
*
|
|
@@ -88,17 +194,51 @@ export class PaginationFilterField {
|
|
|
88
194
|
*/
|
|
89
195
|
exists?: boolean;
|
|
90
196
|
|
|
91
|
-
constructor(
|
|
92
|
-
{
|
|
93
|
-
field, value = '', equals = true, exact = true, exists = false
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
) {
|
|
197
|
+
constructor(args: FilterFieldCtorArgs) {
|
|
198
|
+
const {
|
|
199
|
+
field, value = '', equals = true, exact = true, equality = undefined, exists = false
|
|
200
|
+
} = args;
|
|
201
|
+
|
|
97
202
|
this.field = field;
|
|
98
203
|
this.value = value;
|
|
99
204
|
this.equals = equals;
|
|
100
205
|
this.exact = exact;
|
|
101
206
|
this.exists = exists;
|
|
207
|
+
|
|
208
|
+
const _equality = PaginationFilterField.safeEquality({
|
|
209
|
+
field, value, equals, exact, equality, exists
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (_equality) {
|
|
213
|
+
this.equality = _equality;
|
|
214
|
+
} else {
|
|
215
|
+
throw new Error('A pagination filter must have either equals or equality set');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Determine equality for this field.
|
|
221
|
+
*
|
|
222
|
+
* Mainly to ensure legacy objects using deprecated fields instead of new equality field fall back on something sensible
|
|
223
|
+
*/
|
|
224
|
+
static safeEquality(args: FilterFieldCtorArgs | PaginationFilterField): PaginationFilterEquality | undefined {
|
|
225
|
+
if (args.equality) {
|
|
226
|
+
return args.equality;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (args.equals === true) {
|
|
230
|
+
if (args.exact === true) {
|
|
231
|
+
return PaginationFilterEquality.EQUALS;
|
|
232
|
+
} else {
|
|
233
|
+
return PaginationFilterEquality.CONTAINS;
|
|
234
|
+
}
|
|
235
|
+
} else if (args.equals === false) {
|
|
236
|
+
if (args.exact === true) {
|
|
237
|
+
return PaginationFilterEquality.NOT_EQUALS;
|
|
238
|
+
} else {
|
|
239
|
+
return PaginationFilterEquality.NOT_CONTAINS;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
102
242
|
}
|
|
103
243
|
}
|
|
104
244
|
|
|
@@ -202,7 +342,15 @@ export abstract class PaginationParam {
|
|
|
202
342
|
}
|
|
203
343
|
|
|
204
344
|
/**
|
|
205
|
-
* This is a convenience class for the `filter` param which works some magic, adds defaults and converts to the required PaginationParam format
|
|
345
|
+
* This is a convenience class for the `filter` param which works some magic, adds defaults and converts to the required PaginationParam format.
|
|
346
|
+
*
|
|
347
|
+
* for example
|
|
348
|
+
*
|
|
349
|
+
* - filter=???
|
|
350
|
+
*
|
|
351
|
+
* including `fields` this could be
|
|
352
|
+
*
|
|
353
|
+
* - filter=a=b
|
|
206
354
|
*
|
|
207
355
|
* See description for {@link PaginationParam} for how multiple of these can be combined together to AND or OR together
|
|
208
356
|
*
|
|
@@ -238,7 +386,7 @@ export class PaginationParamFilter extends PaginationParam {
|
|
|
238
386
|
/**
|
|
239
387
|
* Convenience method when you just want an instance of {@link PaginationParamFilter} with a simple `filter=x=y` param
|
|
240
388
|
*/
|
|
241
|
-
static createSingleField(field:
|
|
389
|
+
static createSingleField(field: FilterFieldCtorArgs): PaginationParam {
|
|
242
390
|
return new PaginationParamFilter({ fields: [new PaginationFilterField(field)] });
|
|
243
391
|
}
|
|
244
392
|
|
|
@@ -12,10 +12,17 @@ export interface STEVE_WATCH_EVENT_PARAMS_COMMON {
|
|
|
12
12
|
params: STEVE_WATCH_PARAMS,
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Args for @STEVE_WATCH_EVENT_LISTENER_CALLBACK
|
|
17
|
+
*/
|
|
18
|
+
export type STEVE_WATCH_EVENT_LISTENER_CALLBACK_PARAMS = {
|
|
19
|
+
forceWatch?: boolean,
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
/**
|
|
16
23
|
* Executes when a watch event has a listener and it's triggered
|
|
17
24
|
*/
|
|
18
|
-
export type STEVE_WATCH_EVENT_LISTENER_CALLBACK = () => void
|
|
25
|
+
export type STEVE_WATCH_EVENT_LISTENER_CALLBACK = (params: STEVE_WATCH_EVENT_LISTENER_CALLBACK_PARAMS) => void
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
28
|
* Common params used when a watcher adds a listener to a watch
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BOTTOM, CENTER, LEFT, RIGHT } from '@shell/utils/position';
|
|
2
|
+
|
|
3
|
+
// Defines the possible layouts where the window manager can be used.
|
|
4
|
+
export const enum Layout {
|
|
5
|
+
default = 'default', // eslint-disable-line no-unused-vars
|
|
6
|
+
home = 'home', // eslint-disable-line no-unused-vars
|
|
7
|
+
plain = 'plain', // eslint-disable-line no-unused-vars
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type Position = typeof BOTTOM | typeof LEFT | typeof RIGHT | typeof CENTER;
|
|
11
|
+
|
|
12
|
+
export interface Tab {
|
|
13
|
+
id: string,
|
|
14
|
+
icon: string,
|
|
15
|
+
label: string,
|
|
16
|
+
component?: string,
|
|
17
|
+
extensionId?: string,
|
|
18
|
+
position: Position,
|
|
19
|
+
layouts: Layout[],
|
|
20
|
+
showHeader: boolean,
|
|
21
|
+
containerHeight: number | null,
|
|
22
|
+
containerWidth: number | null,
|
|
23
|
+
attrs?: Record<string, any>,
|
|
24
|
+
}
|
|
@@ -182,6 +182,25 @@ describe('fx: diff', () => {
|
|
|
182
182
|
|
|
183
183
|
expect(result).toStrictEqual(expected);
|
|
184
184
|
});
|
|
185
|
+
it('should return an object with property "baz" different than "null" if using the flag "preventNull" as true', () => {
|
|
186
|
+
const from = {
|
|
187
|
+
foo: 'bar',
|
|
188
|
+
baz: 'bang',
|
|
189
|
+
};
|
|
190
|
+
const to = {
|
|
191
|
+
foo: 'bar',
|
|
192
|
+
bang: 'baz'
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const result = diff(from, to, true);
|
|
196
|
+
const expected = {
|
|
197
|
+
// the property "baz" having value !== null covers test case for https://github.com/rancher/dashboard/issues/15710
|
|
198
|
+
baz: 'bang',
|
|
199
|
+
bang: 'baz'
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
expect(result).toStrictEqual(expected);
|
|
203
|
+
});
|
|
185
204
|
it('should return an object and dot characters in object should still be respected', () => {
|
|
186
205
|
const from = {};
|
|
187
206
|
const to = { foo: { 'bar.baz': 'bang' } };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { getHostedProviders, isHostedProvider } from '../provider';
|
|
2
|
+
import { ClusterProvisionerContext, IClusterProvisioner } from '@shell/core/types';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_CONTEXT = {
|
|
5
|
+
dispatch: {},
|
|
6
|
+
getters: {},
|
|
7
|
+
axios: {},
|
|
8
|
+
t: (args: any) => args.join(' '),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const MOCK_PROVIDERS: IClusterProvisioner[] = [
|
|
12
|
+
{ id: 'AKS', group: 'hosted' } as IClusterProvisioner,
|
|
13
|
+
{ id: 'EKS', group: 'hosted' } as IClusterProvisioner,
|
|
14
|
+
{ id: 'GKE', group: 'hosted' } as IClusterProvisioner,
|
|
15
|
+
{ id: 'alibaba', group: 'hosted' } as IClusterProvisioner,
|
|
16
|
+
{ id: 'other', group: 'other' } as IClusterProvisioner,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
describe('utils/provider', () => {
|
|
20
|
+
describe('getHostedProviders', () => {
|
|
21
|
+
it('should return an empty array when context.$extension is undefined', () => {
|
|
22
|
+
const context = { ...DEFAULT_CONTEXT } as ClusterProvisionerContext;
|
|
23
|
+
const result = getHostedProviders(context);
|
|
24
|
+
|
|
25
|
+
expect(result).toStrictEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should return hosted providers when context.$extension is defined', () => {
|
|
29
|
+
const context = {
|
|
30
|
+
...DEFAULT_CONTEXT,
|
|
31
|
+
$extension: { getProviders: jest.fn().mockReturnValue(MOCK_PROVIDERS) }
|
|
32
|
+
} as unknown as ClusterProvisionerContext;
|
|
33
|
+
|
|
34
|
+
const result = getHostedProviders(context);
|
|
35
|
+
|
|
36
|
+
expect(result).toStrictEqual([
|
|
37
|
+
{ id: 'AKS', group: 'hosted' },
|
|
38
|
+
{ id: 'EKS', group: 'hosted' },
|
|
39
|
+
{ id: 'GKE', group: 'hosted' },
|
|
40
|
+
{ id: 'alibaba', group: 'hosted' },
|
|
41
|
+
]);
|
|
42
|
+
expect(context.$extension.getProviders).toHaveBeenCalledWith(context);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should return an empty array if getProviders returns null', () => {
|
|
46
|
+
const context = {
|
|
47
|
+
...DEFAULT_CONTEXT,
|
|
48
|
+
$extension: { getProviders: jest.fn().mockReturnValue(null) }
|
|
49
|
+
} as unknown as ClusterProvisionerContext;
|
|
50
|
+
|
|
51
|
+
const result = getHostedProviders(context);
|
|
52
|
+
|
|
53
|
+
expect(result).toStrictEqual([]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should return an empty array if getProviders returns undefined', () => {
|
|
57
|
+
const context = {
|
|
58
|
+
...DEFAULT_CONTEXT,
|
|
59
|
+
$extension: { getProviders: jest.fn().mockReturnValue(undefined) }
|
|
60
|
+
} as unknown as ClusterProvisionerContext;
|
|
61
|
+
|
|
62
|
+
const result = getHostedProviders(context);
|
|
63
|
+
|
|
64
|
+
expect(result).toStrictEqual([]);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('isHostedProvider', () => {
|
|
69
|
+
it('should return false if provisioner is not provided', () => {
|
|
70
|
+
const context = {
|
|
71
|
+
...DEFAULT_CONTEXT,
|
|
72
|
+
$extension: { getProviders: jest.fn().mockReturnValue(MOCK_PROVIDERS) }
|
|
73
|
+
} as ClusterProvisionerContext;
|
|
74
|
+
|
|
75
|
+
expect(isHostedProvider(context, '')).toBe(false);
|
|
76
|
+
expect(isHostedProvider(context, undefined as any)).toBe(false);
|
|
77
|
+
expect(isHostedProvider(context, null as any)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return true only if provisioner is in the list of hosted providers', () => {
|
|
81
|
+
const context = {
|
|
82
|
+
...DEFAULT_CONTEXT,
|
|
83
|
+
$extension: { getProviders: jest.fn().mockReturnValue(MOCK_PROVIDERS) }
|
|
84
|
+
} as ClusterProvisionerContext;
|
|
85
|
+
|
|
86
|
+
expect(isHostedProvider(context, 'AKS')).toBe(true);
|
|
87
|
+
expect(isHostedProvider(context, 'eks')).toBe(true);
|
|
88
|
+
expect(isHostedProvider(context, 'different')).toBe(false); // case-insensitive check
|
|
89
|
+
expect(isHostedProvider(context, 'other')).toBe(false); // case-insensitive check
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should return false if there are no hosted providers', () => {
|
|
93
|
+
const context = { ...DEFAULT_CONTEXT, $extension: { getProviders: jest.fn().mockReturnValue([]) } } as ClusterProvisionerContext;
|
|
94
|
+
|
|
95
|
+
expect(isHostedProvider(context, 'prov1')).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { labelSelectorToSelector } from '@shell/utils/selector-typed';
|
|
2
|
+
import { KubeLabelSelector } from '@shell/types/kube/kube-api';
|
|
3
|
+
|
|
4
|
+
describe('selector-typed', () => {
|
|
5
|
+
describe('labelSelectorToSelector', () => {
|
|
6
|
+
describe('empty label selectors', () => {
|
|
7
|
+
it('should return empty string for undefined label selector', () => {
|
|
8
|
+
const result = labelSelectorToSelector(undefined);
|
|
9
|
+
|
|
10
|
+
expect(result).toBe('');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should return empty string for label selector with no matchLabels and no matchExpressions', () => {
|
|
14
|
+
const labelSelector: KubeLabelSelector = {};
|
|
15
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
16
|
+
|
|
17
|
+
expect(result).toBe('');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return empty string for label selector with empty matchLabels', () => {
|
|
21
|
+
const labelSelector: KubeLabelSelector = { matchLabels: {} };
|
|
22
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
23
|
+
|
|
24
|
+
expect(result).toBe('');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return empty string for label selector with empty matchExpressions', () => {
|
|
28
|
+
const labelSelector: KubeLabelSelector = { matchExpressions: [] };
|
|
29
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
30
|
+
|
|
31
|
+
expect(result).toBe('');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return empty string for label selector with both empty matchLabels and matchExpressions', () => {
|
|
35
|
+
const labelSelector: KubeLabelSelector = {
|
|
36
|
+
matchLabels: {},
|
|
37
|
+
matchExpressions: []
|
|
38
|
+
};
|
|
39
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
40
|
+
|
|
41
|
+
expect(result).toBe('');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('matchLabels conversion', () => {
|
|
46
|
+
it('should convert single matchLabel to selector string', () => {
|
|
47
|
+
const labelSelector: KubeLabelSelector = { matchLabels: { app: 'nginx' } };
|
|
48
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
49
|
+
|
|
50
|
+
expect(result).toBe('app=nginx');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should convert multiple matchLabels to comma-separated selector string', () => {
|
|
54
|
+
const labelSelector: KubeLabelSelector = {
|
|
55
|
+
matchLabels: {
|
|
56
|
+
app: 'nginx',
|
|
57
|
+
version: 'v1.0',
|
|
58
|
+
env: 'production'
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
62
|
+
|
|
63
|
+
expect(result).toBe('app=nginx,version=v1.0,env=production');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should handle matchLabels with special characters', () => {
|
|
67
|
+
const labelSelector: KubeLabelSelector = { matchLabels: { 'app.kubernetes.io/name': 'my-app' } };
|
|
68
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
69
|
+
|
|
70
|
+
expect(result).toBe('app.kubernetes.io/name=my-app');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle matchLabels with numeric values', () => {
|
|
74
|
+
const labelSelector: KubeLabelSelector = { matchLabels: { tier: '3' } };
|
|
75
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
76
|
+
|
|
77
|
+
expect(result).toBe('tier=3');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('matchExpressions conversion with In operator', () => {
|
|
82
|
+
it('should convert matchExpression with In operator and single value to equality selector', () => {
|
|
83
|
+
const labelSelector: KubeLabelSelector = {
|
|
84
|
+
matchExpressions: [
|
|
85
|
+
{
|
|
86
|
+
key: 'app',
|
|
87
|
+
operator: 'In',
|
|
88
|
+
values: ['nginx']
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
};
|
|
92
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
93
|
+
|
|
94
|
+
expect(result).toBe('app=nginx');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should convert matchExpression with In operator and multiple values to in() selector', () => {
|
|
98
|
+
const labelSelector: KubeLabelSelector = {
|
|
99
|
+
matchExpressions: [
|
|
100
|
+
{
|
|
101
|
+
key: 'env',
|
|
102
|
+
operator: 'In',
|
|
103
|
+
values: ['dev', 'staging', 'prod']
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
};
|
|
107
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
108
|
+
|
|
109
|
+
expect(result).toBe('env in (dev,staging,prod)');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should convert multiple matchExpressions with In operator', () => {
|
|
113
|
+
const labelSelector: KubeLabelSelector = {
|
|
114
|
+
matchExpressions: [
|
|
115
|
+
{
|
|
116
|
+
key: 'app',
|
|
117
|
+
operator: 'In',
|
|
118
|
+
values: ['nginx']
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
key: 'env',
|
|
122
|
+
operator: 'In',
|
|
123
|
+
values: ['dev', 'staging']
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
128
|
+
|
|
129
|
+
expect(result).toBe('app=nginx,env in (dev,staging)');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should handle matchExpression with empty values array for In operator', () => {
|
|
133
|
+
const labelSelector: KubeLabelSelector = {
|
|
134
|
+
matchExpressions: [
|
|
135
|
+
{
|
|
136
|
+
key: 'app',
|
|
137
|
+
operator: 'In',
|
|
138
|
+
values: []
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
};
|
|
142
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
143
|
+
|
|
144
|
+
// With empty values array, it should create an in() with no values
|
|
145
|
+
expect(result).toBe('app in ()');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('combined matchLabels and matchExpressions', () => {
|
|
150
|
+
it('should combine matchLabels and matchExpressions with single values', () => {
|
|
151
|
+
const labelSelector: KubeLabelSelector = {
|
|
152
|
+
matchLabels: { tier: 'frontend' },
|
|
153
|
+
matchExpressions: [
|
|
154
|
+
{
|
|
155
|
+
key: 'env',
|
|
156
|
+
operator: 'In',
|
|
157
|
+
values: ['prod']
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
};
|
|
161
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
162
|
+
|
|
163
|
+
expect(result).toBe('tier=frontend,env=prod');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should combine multiple matchLabels and matchExpressions', () => {
|
|
167
|
+
const labelSelector: KubeLabelSelector = {
|
|
168
|
+
matchLabels: {
|
|
169
|
+
tier: 'frontend',
|
|
170
|
+
version: 'v2'
|
|
171
|
+
},
|
|
172
|
+
matchExpressions: [
|
|
173
|
+
{
|
|
174
|
+
key: 'env',
|
|
175
|
+
operator: 'In',
|
|
176
|
+
values: ['dev', 'staging']
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
key: 'region',
|
|
180
|
+
operator: 'In',
|
|
181
|
+
values: ['us-west-1']
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
};
|
|
185
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
186
|
+
|
|
187
|
+
expect(result).toBe('tier=frontend,version=v2,env in (dev,staging),region=us-west-1');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should combine matchLabels with multiple matchExpressions using in() notation', () => {
|
|
191
|
+
const labelSelector: KubeLabelSelector = {
|
|
192
|
+
matchLabels: { 'app.kubernetes.io/name': 'myapp' },
|
|
193
|
+
matchExpressions: [
|
|
194
|
+
{
|
|
195
|
+
key: 'env',
|
|
196
|
+
operator: 'In',
|
|
197
|
+
values: ['dev', 'test', 'prod']
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
};
|
|
201
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
202
|
+
|
|
203
|
+
expect(result).toBe('app.kubernetes.io/name=myapp,env in (dev,test,prod)');
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('unsupported operators', () => {
|
|
208
|
+
it('should throw error for NotIn operator', () => {
|
|
209
|
+
const labelSelector: KubeLabelSelector = {
|
|
210
|
+
matchExpressions: [
|
|
211
|
+
{
|
|
212
|
+
key: 'env',
|
|
213
|
+
operator: 'NotIn',
|
|
214
|
+
values: ['prod']
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
expect(() => labelSelectorToSelector(labelSelector)).toThrow('Unsupported matchExpression found when converting to selector string.');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('edge cases', () => {
|
|
224
|
+
it('should handle matchExpression with In operator but undefined values', () => {
|
|
225
|
+
const labelSelector: KubeLabelSelector = {
|
|
226
|
+
matchExpressions: [
|
|
227
|
+
{
|
|
228
|
+
key: 'app',
|
|
229
|
+
operator: 'In',
|
|
230
|
+
values: undefined
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// When values is undefined, the function throws an error
|
|
236
|
+
expect(() => labelSelectorToSelector(labelSelector)).toThrow('Unsupported matchExpression found when converting to selector string.');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should preserve order of matchLabels and matchExpressions', () => {
|
|
240
|
+
const labelSelector: KubeLabelSelector = {
|
|
241
|
+
matchLabels: {
|
|
242
|
+
first: 'value1',
|
|
243
|
+
second: 'value2'
|
|
244
|
+
},
|
|
245
|
+
matchExpressions: [
|
|
246
|
+
{
|
|
247
|
+
key: 'third',
|
|
248
|
+
operator: 'In',
|
|
249
|
+
values: ['value3']
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
};
|
|
253
|
+
const result = labelSelectorToSelector(labelSelector);
|
|
254
|
+
|
|
255
|
+
// matchLabels come before matchExpressions
|
|
256
|
+
expect(result).toContain('first=value1');
|
|
257
|
+
expect(result).toContain('second=value2');
|
|
258
|
+
expect(result).toContain('third=value3');
|
|
259
|
+
expect(result.indexOf('first')).toBeLessThan(result.indexOf('third'));
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isDevBuild } from '@shell/utils/version';
|
|
1
|
+
import { isDevBuild, isUpgradeFromPreToStable } from '@shell/utils/version';
|
|
2
2
|
|
|
3
3
|
describe('fx: isDevBuild', () => {
|
|
4
4
|
it.each([
|
|
@@ -16,3 +16,21 @@ describe('fx: isDevBuild', () => {
|
|
|
16
16
|
}
|
|
17
17
|
);
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
describe('fx: isUpgradeFromPreToStable', () => {
|
|
21
|
+
it('should be true when going from pre-release to stable of same version', () => {
|
|
22
|
+
expect(isUpgradeFromPreToStable('1.0.0-rc1', '1.0.0')).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should be false when going from stable to pre-release', () => {
|
|
26
|
+
expect(isUpgradeFromPreToStable('1.0.0', '1.0.0-rc1')).toBe(false );
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should be false for stable to stable', () => {
|
|
30
|
+
expect(isUpgradeFromPreToStable('1.0.0', '1.1.0')).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should be false for pre-release to pre-release', () => {
|
|
34
|
+
expect(isUpgradeFromPreToStable('1.0.0-rc1', '1.0.0-rc2')).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|