@rancher/shell 3.0.4 → 3.0.5-rc.2
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/images/providers/sks.svg +1 -0
- package/assets/styles/base/_basic.scss +6 -0
- package/assets/styles/base/_helpers.scss +4 -0
- package/assets/styles/base/_variables.scss +1 -0
- package/assets/styles/global/_button.scss +1 -0
- package/assets/translations/en-us.yaml +65 -15
- package/assets/translations/zh-hans.yaml +4 -3
- package/chart/monitoring/index.vue +3 -1
- package/cloud-credential/aws.vue +2 -0
- package/components/ActionDropdownShell.vue +71 -0
- package/components/AppModal.vue +18 -4
- package/components/AsyncButton.vue +24 -7
- package/components/BannerGraphic.vue +1 -0
- package/components/CommunityLinks.vue +4 -59
- package/components/CopyToClipboardText.vue +2 -1
- package/components/CruResource.vue +6 -1
- package/components/DetailText.vue +5 -0
- package/components/ExplorerMembers.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +68 -18
- package/components/GlobalRoleBindings.vue +5 -1
- package/components/GrowlManager.vue +1 -0
- package/components/LandingPagePreference.vue +7 -3
- package/components/LocaleSelector.vue +39 -95
- package/components/ModalManager.vue +55 -0
- package/components/ModalWithCard.vue +1 -0
- package/components/PromptModal.vue +47 -8
- package/components/PromptRemove.vue +1 -0
- package/components/PromptRestore.vue +1 -0
- package/components/ResourceCancelModal.vue +1 -0
- package/components/ResourceDetail/Masthead.vue +38 -12
- package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
- package/components/ResourceDetail/index.vue +47 -12
- package/components/ResourceTable.vue +54 -19
- package/components/SideNav.vue +5 -1
- package/components/SlideInPanelManager.vue +126 -0
- package/components/SortableTable/THead.vue +5 -2
- package/components/SortableTable/actions.js +1 -1
- package/components/SortableTable/index.vue +64 -51
- package/components/SortableTable/paging.js +16 -19
- package/components/SortableTable/selection.js +0 -11
- package/components/Wizard.vue +2 -2
- package/components/__tests__/AsyncButton.test.ts +2 -2
- package/components/__tests__/ModalManager.spec.ts +176 -0
- package/components/__tests__/PromptModal.test.ts +148 -0
- package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
- package/components/auth/AuthBanner.vue +13 -11
- package/components/auth/Principal.vue +1 -0
- package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
- package/components/auth/login/ldap.vue +1 -1
- package/components/fleet/FleetResources.vue +21 -6
- package/components/form/ArrayList.vue +76 -60
- package/components/form/BannerSettings.vue +17 -2
- package/components/form/ColorInput.vue +35 -6
- package/components/form/Command.vue +6 -15
- package/components/form/EnvVars.vue +16 -8
- package/components/form/HealthCheck.vue +3 -3
- package/components/form/HookOption.vue +11 -16
- package/components/form/LabeledSelect.vue +18 -22
- package/components/form/LifecycleHooks.vue +3 -3
- package/components/form/MatchExpressions.vue +14 -8
- package/components/form/NameNsDescription.vue +128 -104
- package/components/form/Networking.vue +20 -12
- package/components/form/NodeAffinity.vue +31 -23
- package/components/form/NodeScheduling.vue +13 -3
- package/components/form/NotificationSettings.vue +15 -1
- package/components/form/Password.vue +1 -0
- package/components/form/PodAffinity.vue +43 -43
- package/components/form/Probe.vue +68 -66
- package/components/form/ResourceQuota/Project.vue +5 -1
- package/components/form/ResourceSelector.vue +7 -9
- package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +16 -24
- package/components/form/SSHKnownHosts/index.vue +30 -13
- package/components/form/Security.vue +54 -56
- package/components/form/Select.vue +32 -21
- package/components/form/ShellInput.vue +5 -1
- package/components/form/Tolerations.vue +5 -1
- package/components/form/ValueFromResource.vue +134 -121
- package/components/form/WorkloadPorts.vue +18 -18
- package/components/form/__tests__/ArrayList.test.ts +5 -2
- package/components/form/__tests__/ColorInput.test.ts +35 -0
- package/components/form/__tests__/LabeledSelect.test.ts +40 -0
- package/components/form/__tests__/MatchExpressions.test.ts +12 -12
- package/components/form/__tests__/NameNsDescription.test.ts +115 -14
- package/components/form/__tests__/Probe.test.ts +12 -8
- package/components/form/__tests__/SSHKnownHosts.test.ts +22 -2
- package/components/form/__tests__/Select.test.ts +37 -0
- package/components/formatter/InternalExternalIP.vue +2 -0
- package/components/formatter/SecretData.vue +20 -7
- package/components/nav/Group.vue +27 -5
- package/components/nav/Header.vue +17 -43
- package/components/nav/NamespaceFilter.vue +134 -86
- package/components/nav/TopLevelMenu.vue +4 -5
- package/components/nav/Type.vue +12 -1
- package/components/nav/WindowManager/ContainerLogs.vue +87 -61
- package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
- package/components/templates/blank.vue +4 -1
- package/components/templates/default.vue +8 -3
- package/components/templates/home.vue +10 -1
- package/components/templates/plain.vue +10 -4
- package/composables/focusTrap.ts +12 -4
- package/composables/useRuntimeFlag.ts +29 -0
- package/config/router/routes.js +20 -13
- package/config/store.js +4 -0
- package/config/uiplugins.js +5 -1
- package/core/types.ts +12 -6
- package/detail/catalog.cattle.io.app.vue +6 -1
- package/detail/fleet.cattle.io.bundle.vue +70 -6
- package/detail/fleet.cattle.io.gitrepo.vue +1 -1
- package/detail/namespace.vue +0 -3
- package/detail/node.vue +17 -13
- package/detail/provisioning.cattle.io.cluster.vue +72 -6
- package/dialog/AddCustomBadgeDialog.vue +1 -1
- package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
- package/{components/AssignTo.vue → dialog/AssignToDialog.vue} +71 -80
- package/dialog/ChangePasswordDialog.vue +106 -0
- package/dialog/DeactivateDriverDialog.vue +1 -0
- package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
- package/dialog/DisableAuthProviderDialog.vue +101 -0
- package/dialog/DrainNode.vue +1 -1
- package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
- package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +69 -57
- package/dialog/FeatureFlagListDialog.vue +288 -0
- package/dialog/ForceMachineRemoveDialog.vue +5 -2
- package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
- package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
- package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -59
- package/dialog/MoveNamespaceDialog.vue +157 -0
- package/dialog/ScalePoolDownDialog.vue +1 -1
- package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
- package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
- package/dialog/WechatDialog.vue +57 -0
- package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
- package/edit/auth/__tests__/oidc.test.ts +152 -109
- package/edit/auth/azuread.vue +2 -1
- package/edit/auth/github.vue +1 -1
- package/edit/auth/googleoauth.vue +5 -1
- package/edit/auth/ldap/index.vue +1 -1
- package/edit/auth/oidc.vue +38 -5
- package/edit/auth/saml.vue +1 -1
- package/edit/cloudcredential.vue +24 -9
- package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
- package/edit/management.cattle.io.user.vue +28 -3
- package/edit/namespace.vue +1 -4
- package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
- package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
- package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -9
- package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
- package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +49 -41
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +33 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
- package/edit/token.vue +2 -0
- package/edit/workload/index.vue +1 -0
- package/edit/workload/mixins/workload.js +0 -2
- package/initialize/install-plugins.js +2 -1
- package/list/harvesterhci.io.management.cluster.vue +4 -1
- package/list/management.cattle.io.feature.vue +4 -287
- package/list/provisioning.cattle.io.cluster.vue +20 -12
- package/machine-config/azure.vue +16 -4
- package/mixins/vue-select-overrides.js +0 -4
- package/models/__tests__/namespace.test.ts +25 -1
- package/models/cloudcredential.js +5 -0
- package/models/fleet.cattle.io.cluster.js +8 -2
- package/models/fleet.cattle.io.gitrepo.js +8 -34
- package/models/kontainerdriver.js +6 -3
- package/models/management.cattle.io.feature.js +7 -1
- package/models/management.cattle.io.node.js +3 -3
- package/models/namespace.js +11 -6
- package/models/nodedriver.js +6 -3
- package/models/workload.js +4 -1
- package/package.json +3 -3
- package/pages/about.vue +13 -3
- package/pages/account/index.vue +16 -6
- package/pages/auth/login.vue +18 -7
- package/pages/auth/logout.vue +4 -1
- package/pages/auth/setup.vue +2 -0
- package/pages/auth/verify.vue +13 -8
- package/pages/c/_cluster/apps/charts/chart.vue +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +26 -26
- package/pages/c/_cluster/auth/config/index.vue +10 -12
- package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
- package/pages/c/_cluster/explorer/index.vue +17 -15
- package/pages/c/_cluster/istio/index.vue +2 -2
- package/pages/c/_cluster/longhorn/index.vue +1 -1
- package/pages/c/_cluster/monitoring/index.vue +1 -1
- package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
- package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
- package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
- package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
- package/pages/c/_cluster/neuvector/index.vue +1 -1
- package/pages/c/_cluster/settings/banners.vue +4 -3
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
- package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
- package/pages/c/_cluster/uiplugins/index.vue +98 -55
- package/pages/diagnostic.vue +59 -11
- package/pages/fail-whale.vue +14 -8
- package/pages/home.vue +24 -18
- package/pages/prefs.vue +7 -6
- package/pages/support/index.vue +4 -1
- package/plugins/internal-api/index.ts +37 -0
- package/plugins/internal-api/shared/base-api.ts +13 -0
- package/plugins/internal-api/shell/shell.api.ts +108 -0
- package/plugins/steve/actions.js +0 -12
- package/public/index.html +1 -0
- package/rancher-components/Card/Card.vue +1 -1
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +47 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
- package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
- package/rancher-components/Form/Radio/RadioButton.vue +20 -4
- package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
- package/rancher-components/Form/Radio/RadioGroup.vue +52 -10
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +5 -0
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
- package/rancher-components/RcButton/RcButton.vue +2 -1
- package/rancher-components/RcButton/types.ts +1 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +18 -6
- package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
- package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -0
- package/rancher-components/RcDropdown/index.ts +2 -0
- package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
- package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
- package/scripts/extension/bundle +20 -0
- package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
- package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
- package/scripts/extension/helmpatch +44 -31
- package/scripts/extension/publish +12 -12
- package/scripts/typegen.sh +2 -4
- package/server/har-file.js +25 -3
- package/store/action-menu.js +26 -56
- package/store/features.js +2 -1
- package/store/index.js +5 -0
- package/store/modal.ts +71 -0
- package/store/slideInPanel.ts +47 -0
- package/store/type-map.js +12 -1
- package/store/type-map.utils.ts +4 -4
- package/types/global-vue.d.ts +5 -0
- package/types/internal-api/shell/growl.d.ts +25 -0
- package/types/internal-api/shell/modal.d.ts +77 -0
- package/types/internal-api/shell/slideIn.d.ts +15 -0
- package/types/resources/fleet.d.ts +0 -14
- package/types/shell/index.d.ts +43 -24
- package/types/vue-shim.d.ts +4 -1
- package/utils/__mocks__/tabbable.js +13 -0
- package/utils/__tests__/object.test.ts +38 -4
- package/utils/cluster.js +35 -0
- package/utils/fleet.ts +15 -73
- package/utils/object.js +48 -5
- package/utils/validators/formRules/__tests__/index.test.ts +10 -1
- package/utils/validators/formRules/index.ts +27 -3
- package/utils/validators/machine-pool.ts +20 -0
- package/components/DisableAuthProviderModal.vue +0 -114
- package/components/MoveModal.vue +0 -166
- package/components/PromptChangePassword.vue +0 -123
- package/components/fleet/FleetBundleResources.vue +0 -86
- package/components/formatter/ExtensionCache.vue +0 -74
- package/components/formatter/Port.vue +0 -24
- package/components/formatter/SecretType.vue +0 -41
- package/types/vue-shim.d +0 -20
package/store/type-map.js
CHANGED
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
// graphConfig: undefined -- Use this to pass along the graph configuration
|
|
108
108
|
// notFilterNamespace: undefined -- Define namespaces that do not need to be filtered
|
|
109
109
|
// localOnly: False -- Hide this type from the nav/search bar on downstream clusters
|
|
110
|
+
// custom: any - Custom options for a given type
|
|
110
111
|
// }
|
|
111
112
|
// )
|
|
112
113
|
// ignoreGroup(group): Never show group or any types in it
|
|
@@ -524,6 +525,7 @@ export const getters = {
|
|
|
524
525
|
depaginate: false,
|
|
525
526
|
customRoute: undefined,
|
|
526
527
|
resourceEditMasthead: true,
|
|
528
|
+
custom: {},
|
|
527
529
|
};
|
|
528
530
|
|
|
529
531
|
return (schemaOrType, pagination) => {
|
|
@@ -657,7 +659,14 @@ export const getters = {
|
|
|
657
659
|
|
|
658
660
|
const label = typeObj.labelKey ? rootGetters['i18n/t'](typeObj.labelKey) || typeObj.label : typeObj.label;
|
|
659
661
|
|
|
660
|
-
|
|
662
|
+
let labelDisplay = highlightLabel(label, count, typeObj.schema);
|
|
663
|
+
|
|
664
|
+
// If we did not match on just the label, add the schema name and see if that matches
|
|
665
|
+
if (!labelDisplay && typeObj.schema?.attributes) {
|
|
666
|
+
const schemaName = `${ typeObj.schema.attributes.resource }.${ typeObj.schema.attributes.group }`;
|
|
667
|
+
|
|
668
|
+
labelDisplay = highlightLabel(`${ label } (${ schemaName })`, count, typeObj.schema);
|
|
669
|
+
}
|
|
661
670
|
|
|
662
671
|
if ( !labelDisplay ) {
|
|
663
672
|
// Search happens in highlight and returns null if not found
|
|
@@ -1729,6 +1738,8 @@ export const mutations = {
|
|
|
1729
1738
|
let obj = { ...options, match };
|
|
1730
1739
|
|
|
1731
1740
|
if ( idx >= 0 ) {
|
|
1741
|
+
// Merge the custom data object - multiple configures will update existing rather than overwrite
|
|
1742
|
+
obj.custom = Object.assign(state.typeOptions[idx].custom || {}, obj.custom || {});
|
|
1732
1743
|
obj = Object.assign(state.typeOptions[idx], obj);
|
|
1733
1744
|
state.typeOptions.splice(idx, 1, obj);
|
|
1734
1745
|
} else {
|
package/store/type-map.utils.ts
CHANGED
|
@@ -62,12 +62,12 @@ export function createHeaders(
|
|
|
62
62
|
} = columns;
|
|
63
63
|
const { rootGetters } = ctx;
|
|
64
64
|
const out = typeOptions.showState ? [stateColumn] : [];
|
|
65
|
-
const attributes = (schema
|
|
66
|
-
const columnsFromSchema = attributes
|
|
65
|
+
const attributes = (schema?.attributes as SchemaAttribute) || {};
|
|
66
|
+
const columnsFromSchema = attributes?.columns || [];
|
|
67
67
|
|
|
68
68
|
// A specific list has been provided
|
|
69
|
-
if ( headers?.[schema
|
|
70
|
-
return headers[schema
|
|
69
|
+
if ( headers?.[schema?.id]?.length ) {
|
|
70
|
+
return headers[schema?.id].map((entry: any) => {
|
|
71
71
|
if ( typeof entry === 'string' ) {
|
|
72
72
|
const col = findBy(columnsFromSchema, 'name', entry);
|
|
73
73
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface DetailedMessage {
|
|
2
|
+
title?: string;
|
|
3
|
+
description: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface GrowlConfig {
|
|
7
|
+
/**
|
|
8
|
+
* The content of the notification message.
|
|
9
|
+
* Either a simple string or an object with `title` and `description` for detailed notifications.
|
|
10
|
+
*/
|
|
11
|
+
message: string | DetailedMessage;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Optional type of the growl notification.
|
|
15
|
+
* Determines the visual style of the notification.
|
|
16
|
+
* Defaults to `'error'` if not provided.
|
|
17
|
+
*/
|
|
18
|
+
type?: 'success' | 'info' | 'warning' | 'error';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Optional duration (in milliseconds) for which the notification should be displayed.
|
|
22
|
+
* Defaults to `5000` milliseconds. A value of `0` keeps the notification indefinitely.
|
|
23
|
+
*/
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Component } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration object for opening a modal.
|
|
5
|
+
*/
|
|
6
|
+
export interface ModalConfig {
|
|
7
|
+
/**
|
|
8
|
+
* The Vue component to be displayed inside the modal.
|
|
9
|
+
* This can be any SFC (Single-File Component) imported and passed in as a `Component`.
|
|
10
|
+
*
|
|
11
|
+
* Example:
|
|
12
|
+
* ```ts
|
|
13
|
+
* import MyCustomModal from '@/components/MyCustomModal.vue';
|
|
14
|
+
*
|
|
15
|
+
* this.$shell.modal({
|
|
16
|
+
* component: MyCustomModal,
|
|
17
|
+
* componentProps: { title: 'Hello Modal' }
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
component: Component;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Optional props to pass directly to the component rendered inside the modal.
|
|
25
|
+
*
|
|
26
|
+
* Example:
|
|
27
|
+
* ```ts
|
|
28
|
+
* componentProps: { title: 'Hello Modal', isVisible: true }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
componentProps?: Record<string, any>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Optional array of resources that the modal component might need.
|
|
35
|
+
* These are passed directly into the modal's `resources` prop.
|
|
36
|
+
*
|
|
37
|
+
* Example:
|
|
38
|
+
* ```ts
|
|
39
|
+
* resources: [myResource, anotherResource]
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
resources?: any[];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Custom width for the modal. Defaults to `600px`.
|
|
46
|
+
* The width can be specified as a string with a valid unit (`px`, `%`, `rem`, etc.).
|
|
47
|
+
*
|
|
48
|
+
* Examples:
|
|
49
|
+
* ```ts
|
|
50
|
+
* modalWidth: '800px' // Width in pixels
|
|
51
|
+
* modalWidth: '75%' // Width as a percentage
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
modalWidth?: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Determines if clicking outside the modal will close it. Defaults to `true`.
|
|
58
|
+
* Set this to `false` to prevent closing via outside clicks.
|
|
59
|
+
*
|
|
60
|
+
* Example:
|
|
61
|
+
* ```ts
|
|
62
|
+
* closeOnClickOutside: false
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
closeOnClickOutside?: boolean;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* If true, the modal is considered "sticky" and may not close automatically
|
|
69
|
+
* on certain user interactions. Defaults to `false`.
|
|
70
|
+
*
|
|
71
|
+
* Example:
|
|
72
|
+
* ```ts
|
|
73
|
+
* modalSticky: true
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
// modalSticky?: boolean; // Not implemented yet
|
|
77
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Component } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration object for opening a slide-in panel.
|
|
5
|
+
*
|
|
6
|
+
* @property component - The Vue component to render in the slide-in panel.
|
|
7
|
+
* This should be a valid Vue Component, such as an imported SFC or functional component.
|
|
8
|
+
*
|
|
9
|
+
* @property componentProps - (Optional) An object containing props to be passed to the component rendered in the slide-in panel.
|
|
10
|
+
* Keys should match the props defined in the provided component.
|
|
11
|
+
*/
|
|
12
|
+
export interface SlideInConfig {
|
|
13
|
+
component: Component | null;
|
|
14
|
+
componentProps?: Record<string, any>;
|
|
15
|
+
}
|
|
@@ -19,11 +19,6 @@ export interface BundleNonReadyResource extends BundleResourceKey {
|
|
|
19
19
|
summary: { [state: string]: string }
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export interface BundleNonReadyBundle {
|
|
23
|
-
modifiedStatus: BundleModifiedResource[],
|
|
24
|
-
nonReadyStatus: BundleNonReadyResource[],
|
|
25
|
-
}
|
|
26
|
-
|
|
27
22
|
export interface Condition {
|
|
28
23
|
status: string,
|
|
29
24
|
type: string,
|
|
@@ -39,15 +34,6 @@ export interface BundleDeploymentStatus {
|
|
|
39
34
|
conditions: Condition[],
|
|
40
35
|
}
|
|
41
36
|
|
|
42
|
-
export interface BundleStatusSummary {
|
|
43
|
-
nonReadyResources?: BundleNonReadyBundle[],
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface BundleStatus {
|
|
47
|
-
resourceKey?: BundleResourceKey[],
|
|
48
|
-
summary?: BundleStatusSummary,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
37
|
export interface BundleDeployment {
|
|
52
38
|
spec: {
|
|
53
39
|
deploymentId: string,
|
package/types/shell/index.d.ts
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
// Auto-generated type definitions for shell
|
|
2
2
|
// Do not modify this file as changes will get overwritten
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export default Vue;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// This is required to keep typescript from complaining. It is required for
|
|
9
|
-
// our i18n plugin. For more info see:
|
|
10
|
-
// https://v2.vuejs.org/v2/guide/typescript.html?redirect=true#Augmenting-Types-for-Use-with-Plugins
|
|
11
|
-
declare module 'vue/types/vue' {
|
|
12
|
-
// eslint-disable-next-line no-unused-vars
|
|
13
|
-
interface Vue {
|
|
14
|
-
/**
|
|
15
|
-
* Lookup a given string with the given arguments
|
|
16
|
-
* @param raw if set, do not do HTML escaping.
|
|
17
|
-
*/
|
|
18
|
-
t: (key: string, args?: Record<string, any>, raw?: boolean) => string,
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
declare module 'js-yaml';
|
|
3
|
+
/// <reference types="@rancher/shell/types/vue-shim" />
|
|
4
|
+
/// <reference types="@rancher/shell/types/global-vue" />
|
|
23
5
|
|
|
24
6
|
// @shell/config/labels-annotations
|
|
25
7
|
|
|
@@ -2475,7 +2457,7 @@ export default class Namespace {
|
|
|
2475
2457
|
get isObscure(): any;
|
|
2476
2458
|
get projectId(): any;
|
|
2477
2459
|
get project(): any;
|
|
2478
|
-
get
|
|
2460
|
+
get groupById(): any;
|
|
2479
2461
|
get projectNameSort(): any;
|
|
2480
2462
|
get istioInstalled(): boolean;
|
|
2481
2463
|
get injectionEnabled(): boolean;
|
|
@@ -3498,6 +3480,13 @@ export function abbreviateClusterName(input: string): string;
|
|
|
3498
3480
|
export function labelForAddon(store: any, name: any, configuration?: boolean): any;
|
|
3499
3481
|
export function filterOutDeprecatedPatchVersions(allVersions: any, currentVersion: any): any;
|
|
3500
3482
|
export function getAllOptionsAfterCurrentVersion(store: any, versions: any, currentVersion: any, defaultVersion: any): any;
|
|
3483
|
+
export function initSchedulingCustomization(value: any, features: any, store: any, mode: any): Promise<{
|
|
3484
|
+
clusterAgentDefaultPC: any;
|
|
3485
|
+
clusterAgentDefaultPDB: any;
|
|
3486
|
+
schedulingCustomizationFeatureEnabled: any;
|
|
3487
|
+
schedulingCustomizationOriginallyEnabled: boolean;
|
|
3488
|
+
errors: any[];
|
|
3489
|
+
}>;
|
|
3501
3490
|
}
|
|
3502
3491
|
|
|
3503
3492
|
// @shell/utils/color
|
|
@@ -3980,7 +3969,8 @@ export function dropKeys(obj: any, keys: any): void;
|
|
|
3980
3969
|
*/
|
|
3981
3970
|
export function deepToRaw(obj: any, cache?: any): any;
|
|
3982
3971
|
/**
|
|
3983
|
-
* Helper function to alter Lodash merge function default behaviour on merging
|
|
3972
|
+
* Helper function to alter Lodash merge function default behaviour on merging
|
|
3973
|
+
* arrays and objects.
|
|
3984
3974
|
*
|
|
3985
3975
|
* In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
|
|
3986
3976
|
* merging the latest configuration received from the backend with the current configuration updated by the user.
|
|
@@ -3992,10 +3982,39 @@ export function deepToRaw(obj: any, cache?: any): any;
|
|
|
3992
3982
|
* merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
|
|
3993
3983
|
*
|
|
3994
3984
|
* More info: https://github.com/lodash/lodash/issues/1313
|
|
3995
|
-
|
|
3985
|
+
|
|
3996
3986
|
* This helper function addresses the issue by always replacing the old array with the new array during the merge process.
|
|
3987
|
+
*
|
|
3988
|
+
* This helper is also used for another case in rke2.vue to handle merging addon chart default values with the user's current values.
|
|
3989
|
+
* It fixed https://github.com/rancher/dashboard/issues/12418
|
|
3990
|
+
*
|
|
3991
|
+
* If `mutateOriginal` is true, the merge is done directly into `obj1` (mutating it).
|
|
3992
|
+
* This is useful in cases like:
|
|
3993
|
+
* machinePool.config = mergeWithReplace(clonedLatestConfig, clonedCurrentConfig, { mutateOriginal: true })
|
|
3994
|
+
* where merging into a new empty object may lead to incomplete structure.
|
|
3995
|
+
*
|
|
3996
|
+
* Use `mutateOriginal` when you want to preserve obj1’s original shape, references,
|
|
3997
|
+
* or when assigning the result directly to an existing object.
|
|
3998
|
+
*
|
|
3999
|
+
* @param {Object} [obj1={}] - The first object to merge
|
|
4000
|
+
* @param {Object} [obj2={}] - The second object to merge
|
|
4001
|
+
* @param {Object} [options={}] - Options for customizing merge behavior
|
|
4002
|
+
* @param {boolean} [options.mutateOriginal=false] - true: mutates obj1
|
|
4003
|
+
* directly. false: returns a new object
|
|
4004
|
+
* @param {boolean} [options.replaceArray=true] - true: replaces arrays in obj1
|
|
4005
|
+
* with arrays in obj2 when both properties are arrays
|
|
4006
|
+
* false: default lodash merge behavior - recursively merges
|
|
4007
|
+
* array members
|
|
4008
|
+
* @param {boolean} [options.replaceObjectProps=false] - true: merges objects in
|
|
4009
|
+
* obj1 with objects in obj2, overwriting duplicate props
|
|
4010
|
+
* false: default lodash merge behavior - recursively merges
|
|
4011
|
+
* object props
|
|
3997
4012
|
*/
|
|
3998
|
-
export function
|
|
4013
|
+
export function mergeWithReplace(obj1?: any, obj2?: any, { mutateOriginal, replaceArray, replaceObjectProps, }?: {
|
|
4014
|
+
mutateOriginal?: boolean;
|
|
4015
|
+
replaceArray?: boolean;
|
|
4016
|
+
replaceObjectProps?: boolean;
|
|
4017
|
+
}): any;
|
|
3999
4018
|
export { isEqualBasic as isEqual };
|
|
4000
4019
|
export function toDictionary(array: any, callback: any): any;
|
|
4001
4020
|
/**
|
package/types/vue-shim.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
+
import type ShellApi from '@shell/plugins/internal-api/shell/shell.api';
|
|
3
|
+
|
|
2
4
|
export {};
|
|
3
5
|
|
|
4
6
|
declare module 'vue' {
|
|
@@ -15,6 +17,7 @@ declare module 'vue' {
|
|
|
15
17
|
getters: Record<string, any>,
|
|
16
18
|
dispatch: (action: string, payload?: any) => Promise<any>,
|
|
17
19
|
commit: (mutation: string, payload?: any) => void,
|
|
18
|
-
}
|
|
20
|
+
},
|
|
21
|
+
$shell: ShellApi,
|
|
19
22
|
}
|
|
20
23
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// __mocks__/tabbable.js
|
|
2
|
+
|
|
3
|
+
const lib = jest.requireActual('tabbable');
|
|
4
|
+
|
|
5
|
+
const tabbable = {
|
|
6
|
+
...lib,
|
|
7
|
+
tabbable: (node, options) => lib.tabbable(node, { ...options, displayCheck: 'none' }),
|
|
8
|
+
focusable: (node, options) => lib.focusable(node, { ...options, displayCheck: 'none' }),
|
|
9
|
+
isFocusable: (node, options) => lib.isFocusable(node, { ...options, displayCheck: 'none' }),
|
|
10
|
+
isTabbable: (node, options) => lib.isTabbable(node, { ...options, displayCheck: 'none' }),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
module.exports = tabbable;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { reactive, isReactive } from 'vue';
|
|
2
2
|
import {
|
|
3
|
-
clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys, deepToRaw,
|
|
3
|
+
clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys, deepToRaw, mergeWithReplace
|
|
4
4
|
} from '@shell/utils/object';
|
|
5
5
|
|
|
6
6
|
describe('fx: get', () => {
|
|
@@ -379,7 +379,7 @@ describe('fx: deepToRaw', () => {
|
|
|
379
379
|
});
|
|
380
380
|
});
|
|
381
381
|
|
|
382
|
-
describe('fx:
|
|
382
|
+
describe('fx: mergeWithReplace', () => {
|
|
383
383
|
const testCases: Array<[object?, object?, object?]> = [
|
|
384
384
|
// Some array test cases, an array from the first object should be replaced with the array from the second object
|
|
385
385
|
[{ a: ['one'] }, { a: [] }, { a: [] }],
|
|
@@ -401,8 +401,42 @@ describe('fx: mergeWithReplaceArrays', () => {
|
|
|
401
401
|
[undefined, {}, {}],
|
|
402
402
|
];
|
|
403
403
|
|
|
404
|
-
it.each(testCases)('should merge properly', (obj1, obj2, expected) => {
|
|
405
|
-
const result =
|
|
404
|
+
it.each(testCases)('should merge arrays properly', (obj1, obj2, expected) => {
|
|
405
|
+
const result = mergeWithReplace(obj1, obj2);
|
|
406
|
+
|
|
407
|
+
expect(result).toStrictEqual(expected);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it.each([
|
|
411
|
+
[
|
|
412
|
+
{ a: { b: false, c: false } }, { a: { b: true, c: null } }, { a: { b: true, c: null } }
|
|
413
|
+
],
|
|
414
|
+
[
|
|
415
|
+
{
|
|
416
|
+
a: [{
|
|
417
|
+
b: 'test', c: 'test', value: true
|
|
418
|
+
}]
|
|
419
|
+
}, {
|
|
420
|
+
a: [{
|
|
421
|
+
b: 'test', c: 'test', operator: 'exists'
|
|
422
|
+
}]
|
|
423
|
+
}, {
|
|
424
|
+
a: [{
|
|
425
|
+
b: 'test', c: 'test', operator: 'exists'
|
|
426
|
+
}]
|
|
427
|
+
}
|
|
428
|
+
],
|
|
429
|
+
[
|
|
430
|
+
{
|
|
431
|
+
a: { enabled: false }, b: { enabled: false }, c: { enabled: false }
|
|
432
|
+
},
|
|
433
|
+
{ c: { enabled: true, stripUnderscores: true } },
|
|
434
|
+
{
|
|
435
|
+
a: { enabled: false }, b: { enabled: false }, c: { enabled: true, stripUnderscores: true }
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
])('should overwrite duplicate object properties when merging objects', (left, right, expected) => {
|
|
439
|
+
const result = mergeWithReplace(left, right, { replaceObjectProps: true });
|
|
406
440
|
|
|
407
441
|
expect(result).toStrictEqual(expected);
|
|
408
442
|
});
|
package/utils/cluster.js
CHANGED
|
@@ -6,6 +6,10 @@ import { SETTING } from '@shell/config/settings';
|
|
|
6
6
|
import { PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
7
7
|
import { compare, sortable } from '@shell/utils/version';
|
|
8
8
|
import { sortBy } from '@shell/utils/sort';
|
|
9
|
+
import { SCHEDULING_CUSTOMIZATION } from '@shell/store/features';
|
|
10
|
+
import { _CREATE, _EDIT } from '@shell/config/query-params';
|
|
11
|
+
import isEmpty from 'lodash/isEmpty';
|
|
12
|
+
import { set } from '@shell/utils/object';
|
|
9
13
|
|
|
10
14
|
/**
|
|
11
15
|
* Combination of paginationFilterHiddenLocalCluster and paginationFilterOnlyKubernetesClusters
|
|
@@ -296,3 +300,34 @@ export function getAllOptionsAfterCurrentVersion(store, versions, currentVersion
|
|
|
296
300
|
|
|
297
301
|
return sortedWithDeprecatedLabel;
|
|
298
302
|
}
|
|
303
|
+
|
|
304
|
+
export async function initSchedulingCustomization(value, features, store, mode) {
|
|
305
|
+
const schedulingCustomizationFeatureEnabled = features(SCHEDULING_CUSTOMIZATION);
|
|
306
|
+
let clusterAgentDefaultPC = null;
|
|
307
|
+
let clusterAgentDefaultPDB = null;
|
|
308
|
+
let schedulingCustomizationOriginallyEnabled = false;
|
|
309
|
+
const errors = [];
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
clusterAgentDefaultPC = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS })).value) || null;
|
|
313
|
+
} catch (e) {
|
|
314
|
+
errors.push(e);
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
clusterAgentDefaultPDB = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET })).value) || null;
|
|
318
|
+
} catch (e) {
|
|
319
|
+
errors.push(e);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (schedulingCustomizationFeatureEnabled && mode === _CREATE && isEmpty(value?.clusterAgentDeploymentCustomization?.schedulingCustomization)) {
|
|
323
|
+
set(value, 'clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: clusterAgentDefaultPC, podDisruptionBudget: clusterAgentDefaultPDB });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (mode === _EDIT && !!value?.clusterAgentDeploymentCustomization?.schedulingCustomization) {
|
|
327
|
+
schedulingCustomizationOriginallyEnabled = true;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
clusterAgentDefaultPC, clusterAgentDefaultPDB, schedulingCustomizationFeatureEnabled, schedulingCustomizationOriginallyEnabled, errors
|
|
332
|
+
};
|
|
333
|
+
}
|
package/utils/fleet.ts
CHANGED
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
BundleResourceKey,
|
|
4
4
|
BundleDeployment,
|
|
5
5
|
BundleDeploymentStatus,
|
|
6
|
-
BundleStatus,
|
|
7
6
|
Condition,
|
|
8
7
|
} from '@shell/types/resources/fleet';
|
|
9
|
-
import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
8
|
+
import { mapStateToEnum, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
10
9
|
import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
|
|
10
|
+
import { NAME as EXPLORER_NAME } from '@shell/config/product/explorer';
|
|
11
11
|
|
|
12
12
|
interface Resource extends BundleDeploymentResource {
|
|
13
13
|
state: string,
|
|
@@ -17,15 +17,6 @@ type Labels = {
|
|
|
17
17
|
[key: string]: string,
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
interface StatesCounter { [state: string]: number }
|
|
21
|
-
|
|
22
|
-
function incr(counter: StatesCounter, state: string) {
|
|
23
|
-
if (!counter[state]) {
|
|
24
|
-
counter[state] = 0;
|
|
25
|
-
}
|
|
26
|
-
counter[state]++;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
20
|
function resourceKey(r: BundleResourceKey): string {
|
|
30
21
|
return `${ r.kind }/${ r.namespace }/${ r.name }`;
|
|
31
22
|
}
|
|
@@ -57,6 +48,19 @@ class Fleet {
|
|
|
57
48
|
return `${ r.apiVersion.split('/', 2)[0] }.${ type }`;
|
|
58
49
|
}
|
|
59
50
|
|
|
51
|
+
detailLocation(r: Resource, mgmtClusterName: string): any {
|
|
52
|
+
return mapStateToEnum(r.state) === STATES_ENUM.MISSING ? undefined : {
|
|
53
|
+
name: `c-cluster-product-resource${ r.namespace ? '-namespace' : '' }-id`,
|
|
54
|
+
params: {
|
|
55
|
+
product: EXPLORER_NAME,
|
|
56
|
+
cluster: mgmtClusterName,
|
|
57
|
+
resource: this.resourceType(r),
|
|
58
|
+
namespace: r.namespace,
|
|
59
|
+
id: r.name,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
/**
|
|
61
65
|
* resourcesFromBundleDeploymentStatus extracts the list of resources deployed by a BundleDeployment
|
|
62
66
|
*/
|
|
@@ -94,68 +98,6 @@ class Fleet {
|
|
|
94
98
|
return modified.concat(Object.values(resources));
|
|
95
99
|
}
|
|
96
100
|
|
|
97
|
-
/**
|
|
98
|
-
* resourcesFromBundleStatus extracts the list of resources deployed by a Bundle
|
|
99
|
-
*/
|
|
100
|
-
resourcesFromBundleStatus(status: BundleStatus): Resource[] {
|
|
101
|
-
// The state of every resource is spread all over the bundle status.
|
|
102
|
-
// resourceKey contains one entry per resource AND cluster (built by Fleet from all the child BundleDeployments).
|
|
103
|
-
// However, those entries do not contain the cluster that they belong to, leading to duplicate entries
|
|
104
|
-
|
|
105
|
-
// 1. Fold resourceKey by using a unique key, initializing counters for multiple occurrences of the same resource
|
|
106
|
-
const resources = (status.resourceKey || []).reduce((res, r) => {
|
|
107
|
-
const k = resourceKey(r);
|
|
108
|
-
|
|
109
|
-
if (!res[k]) {
|
|
110
|
-
res[k] = { r, count: {} };
|
|
111
|
-
}
|
|
112
|
-
incr(res[k].count, STATES_ENUM.READY);
|
|
113
|
-
|
|
114
|
-
return res;
|
|
115
|
-
}, {} as { [resourceKey: string]: { r: BundleResourceKey, count: StatesCounter } });
|
|
116
|
-
|
|
117
|
-
// 2. Non-ready resources are counted differently and may also appear in resourceKey, depending on their state
|
|
118
|
-
for (const bundle of status.summary?.nonReadyResources || []) {
|
|
119
|
-
for (const r of bundle.modifiedStatus || []) {
|
|
120
|
-
const k = resourceKey(r);
|
|
121
|
-
|
|
122
|
-
if (!resources[k]) {
|
|
123
|
-
resources[k] = { r, count: {} };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (r.missing) {
|
|
127
|
-
incr(resources[k].count, STATES_ENUM.MISSING);
|
|
128
|
-
} else if (r.delete) {
|
|
129
|
-
resources[k].count[STATES_ENUM.READY]--;
|
|
130
|
-
incr(resources[k].count, STATES_ENUM.ORPHANED);
|
|
131
|
-
} else {
|
|
132
|
-
resources[k].count[STATES_ENUM.READY]--;
|
|
133
|
-
incr(resources[k].count, STATES_ENUM.MODIFIED);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
for (const r of bundle.nonReadyStatus || []) {
|
|
137
|
-
const k = resourceKey(r);
|
|
138
|
-
const state = r.summary?.state || STATES_ENUM.UNKNOWN;
|
|
139
|
-
|
|
140
|
-
resources[k].count[STATES_ENUM.READY]--;
|
|
141
|
-
incr(resources[k].count, state);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// 3. Unfold back to an array of resources for display
|
|
146
|
-
return Object.values(resources).reduce((res, e) => {
|
|
147
|
-
const { r, count } = e;
|
|
148
|
-
|
|
149
|
-
for (const state in count) {
|
|
150
|
-
for (let x = 0; x < count[state]; x++) {
|
|
151
|
-
res.push(Object.assign({ state }, r));
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return res;
|
|
156
|
-
}, [] as Resource[]);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
101
|
clusterIdFromBundleDeploymentLabels(labels?: Labels): string {
|
|
160
102
|
const clusterNamespace = labels?.[FLEET_LABELS.CLUSTER_NAMESPACE];
|
|
161
103
|
const clusterName = labels?.[FLEET_LABELS.CLUSTER];
|
package/utils/object.js
CHANGED
|
@@ -474,7 +474,8 @@ export function deepToRaw(obj, cache = new WeakSet()) {
|
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
/**
|
|
477
|
-
* Helper function to alter Lodash merge function default behaviour on merging
|
|
477
|
+
* Helper function to alter Lodash merge function default behaviour on merging
|
|
478
|
+
* arrays and objects.
|
|
478
479
|
*
|
|
479
480
|
* In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
|
|
480
481
|
* merging the latest configuration received from the backend with the current configuration updated by the user.
|
|
@@ -486,13 +487,55 @@ export function deepToRaw(obj, cache = new WeakSet()) {
|
|
|
486
487
|
* merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
|
|
487
488
|
*
|
|
488
489
|
* More info: https://github.com/lodash/lodash/issues/1313
|
|
489
|
-
|
|
490
|
+
|
|
490
491
|
* This helper function addresses the issue by always replacing the old array with the new array during the merge process.
|
|
492
|
+
*
|
|
493
|
+
* This helper is also used for another case in rke2.vue to handle merging addon chart default values with the user's current values.
|
|
494
|
+
* It fixed https://github.com/rancher/dashboard/issues/12418
|
|
495
|
+
*
|
|
496
|
+
* If `mutateOriginal` is true, the merge is done directly into `obj1` (mutating it).
|
|
497
|
+
* This is useful in cases like:
|
|
498
|
+
* machinePool.config = mergeWithReplace(clonedLatestConfig, clonedCurrentConfig, { mutateOriginal: true })
|
|
499
|
+
* where merging into a new empty object may lead to incomplete structure.
|
|
500
|
+
*
|
|
501
|
+
* Use `mutateOriginal` when you want to preserve obj1’s original shape, references,
|
|
502
|
+
* or when assigning the result directly to an existing object.
|
|
503
|
+
*
|
|
504
|
+
* @param {Object} [obj1={}] - The first object to merge
|
|
505
|
+
* @param {Object} [obj2={}] - The second object to merge
|
|
506
|
+
* @param {Object} [options={}] - Options for customizing merge behavior
|
|
507
|
+
* @param {boolean} [options.mutateOriginal=false] - true: mutates obj1
|
|
508
|
+
* directly. false: returns a new object
|
|
509
|
+
* @param {boolean} [options.replaceArray=true] - true: replaces arrays in obj1
|
|
510
|
+
* with arrays in obj2 when both properties are arrays
|
|
511
|
+
* false: default lodash merge behavior - recursively merges
|
|
512
|
+
* array members
|
|
513
|
+
* @param {boolean} [options.replaceObjectProps=false] - true: merges objects in
|
|
514
|
+
* obj1 with objects in obj2, overwriting duplicate props
|
|
515
|
+
* false: default lodash merge behavior - recursively merges
|
|
516
|
+
* object props
|
|
491
517
|
*/
|
|
492
|
-
export function
|
|
493
|
-
|
|
494
|
-
|
|
518
|
+
export function mergeWithReplace(
|
|
519
|
+
obj1 = {},
|
|
520
|
+
obj2 = {},
|
|
521
|
+
{
|
|
522
|
+
mutateOriginal = false,
|
|
523
|
+
replaceArray = true,
|
|
524
|
+
replaceObjectProps = false,
|
|
525
|
+
} = {}
|
|
526
|
+
) {
|
|
527
|
+
const destination = mutateOriginal ? obj1 : {};
|
|
528
|
+
|
|
529
|
+
return mergeWith(destination, obj1, obj2, (obj1Value, obj2Value) => {
|
|
530
|
+
if (replaceArray && Array.isArray(obj1Value) && Array.isArray(obj2Value)) {
|
|
495
531
|
return obj2Value;
|
|
496
532
|
}
|
|
533
|
+
|
|
534
|
+
if (replaceObjectProps && isObject(obj1Value) && isObject(obj2Value)) {
|
|
535
|
+
return {
|
|
536
|
+
...obj1Value,
|
|
537
|
+
...obj2Value,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
497
540
|
});
|
|
498
541
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { Translation } from '@shell/types/t';
|
|
1
2
|
import formRulesGenerator from '@shell/utils/validators/formRules';
|
|
2
3
|
|
|
3
|
-
const mockT = (key: string, args: any) => {
|
|
4
|
+
const mockT: Translation = (key: string, args: any) => {
|
|
4
5
|
return JSON.stringify({
|
|
5
6
|
message: key,
|
|
6
7
|
...args
|
|
@@ -8,6 +9,14 @@ const mockT = (key: string, args: any) => {
|
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
describe('formRules', () => {
|
|
12
|
+
it('should use "Value" as default label', () => {
|
|
13
|
+
const validators = formRulesGenerator(mockT, {});
|
|
14
|
+
|
|
15
|
+
const message = validators.required(null);
|
|
16
|
+
|
|
17
|
+
expect(message).toStrictEqual('{\"message\":\"validation.required\",\"key\":\"Value\"}');
|
|
18
|
+
});
|
|
19
|
+
|
|
11
20
|
const formRules = formRulesGenerator(mockT, { key: 'testDisplayKey' });
|
|
12
21
|
|
|
13
22
|
it('"required" : returns undefined when value supplied', () => {
|