@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
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* The code in this file is responsible for adding 'announcement 'notifications driven off of the dynamic content metadata
|
|
4
|
+
*
|
|
5
|
+
* Announcements will be able to be shown in different places in the UI
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import semver from 'semver';
|
|
10
|
+
import { NotificationLevel, Notification } from '@shell/types/notifications';
|
|
11
|
+
import { READ_ANNOUNCEMENTS } from '@shell/store/prefs';
|
|
12
|
+
import { Context, VersionInfo, Announcement } from './types';
|
|
13
|
+
import { DynamicContentAnnouncementHandlerName } from './notification-handler';
|
|
14
|
+
|
|
15
|
+
// Prefixes used in the notifications IDs created here
|
|
16
|
+
export const ANNOUNCEMENT_PREFIX = 'announcement-';
|
|
17
|
+
|
|
18
|
+
const TARGET_NOTIFICATION_CENTER = 'notification';
|
|
19
|
+
const TARGET_HOME_PAGE = 'homepage';
|
|
20
|
+
const ALLOWED_TARGETS = [TARGET_NOTIFICATION_CENTER, TARGET_HOME_PAGE];
|
|
21
|
+
|
|
22
|
+
const ALLOWED_NOTIFICATIONS: Record<string, NotificationLevel> = {
|
|
23
|
+
announcement: NotificationLevel.Announcement,
|
|
24
|
+
info: NotificationLevel.Info,
|
|
25
|
+
warning: NotificationLevel.Warning,
|
|
26
|
+
homepage: NotificationLevel.Hidden,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Main exported function that will process the announcements
|
|
31
|
+
*
|
|
32
|
+
* @param context Context helper providing access to config, logger, store
|
|
33
|
+
* @param announcements Announcement information
|
|
34
|
+
* @param versionInfo Version information
|
|
35
|
+
*/
|
|
36
|
+
export async function processAnnouncements(context: Context, announcements: Announcement[] | undefined, versionInfo: VersionInfo): Promise<void> {
|
|
37
|
+
if (!announcements || !announcements.length || !versionInfo?.version) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { dispatch, getters, logger } = context;
|
|
42
|
+
|
|
43
|
+
// Process each announcement
|
|
44
|
+
await Promise.all(announcements.map(async(announcement: Announcement) => {
|
|
45
|
+
// Check version
|
|
46
|
+
if (announcement.version && !semver.satisfies(versionInfo.version, announcement.version)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check audience (currently only admin or all, but may add more in the future)
|
|
51
|
+
if (announcement.audience === 'admin' && !context.isAdmin) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!announcement.id) {
|
|
56
|
+
logger.error(`No ID For announcement - not going to add a notification for the announcement`);
|
|
57
|
+
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check type
|
|
62
|
+
const targetSplit = announcement.target.split('/');
|
|
63
|
+
const target = targetSplit[0];
|
|
64
|
+
|
|
65
|
+
// Make sure that the target is supported
|
|
66
|
+
if (ALLOWED_TARGETS.includes(target)) {
|
|
67
|
+
let level = NotificationLevel.Announcement;
|
|
68
|
+
let data: any = {};
|
|
69
|
+
|
|
70
|
+
if (target === TARGET_NOTIFICATION_CENTER) {
|
|
71
|
+
// Show a notification
|
|
72
|
+
const subType = targetSplit.length === 2 ? targetSplit[1] : 'announcement';
|
|
73
|
+
|
|
74
|
+
if (!(subType in ALLOWED_NOTIFICATIONS)) {
|
|
75
|
+
logger.error(`Announcement notification type ${ subType } is not supported`);
|
|
76
|
+
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
level = ALLOWED_NOTIFICATIONS[subType];
|
|
81
|
+
} else if (target === TARGET_HOME_PAGE) {
|
|
82
|
+
level = NotificationLevel.Hidden;
|
|
83
|
+
data = {
|
|
84
|
+
icon: announcement.icon,
|
|
85
|
+
location: targetSplit.length === 2 ? targetSplit[1] : 'banner',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if (announcement.style) {
|
|
89
|
+
data.style = announcement.style;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
logger.info(`Going to add a notification for announcement ${ announcement.target }`);
|
|
94
|
+
|
|
95
|
+
// We should check if the notification already exists
|
|
96
|
+
const id = `${ ANNOUNCEMENT_PREFIX }${ announcement.id }`;
|
|
97
|
+
const existing = getters['notifications/item'](id);
|
|
98
|
+
|
|
99
|
+
// Check if the pref for 'read announcements' has the id
|
|
100
|
+
const pref = getters['prefs/get'](READ_ANNOUNCEMENTS) || '';
|
|
101
|
+
const prefExists = pref.split(',').includes(announcement.id);
|
|
102
|
+
|
|
103
|
+
if (existing || prefExists) {
|
|
104
|
+
logger.info(`Not adding announcement with ID ${ id } as it already exists or has been read previously (title: ${ announcement.title })`);
|
|
105
|
+
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const notification: Notification = {
|
|
110
|
+
id,
|
|
111
|
+
level,
|
|
112
|
+
title: announcement.title,
|
|
113
|
+
message: announcement.message,
|
|
114
|
+
handlerName: DynamicContentAnnouncementHandlerName,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (data && Object.keys(data).length > 0) {
|
|
118
|
+
notification.data = data;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (announcement.cta?.primary) {
|
|
122
|
+
notification.primaryAction = {
|
|
123
|
+
label: announcement.cta.primary.action,
|
|
124
|
+
target: announcement.cta.primary.link,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (announcement.cta?.secondary) {
|
|
129
|
+
notification.secondaryAction = {
|
|
130
|
+
label: announcement.cta.secondary.action,
|
|
131
|
+
target: announcement.cta.secondary.link,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
logger.info(`Adding announcement with ID ${ id } (title: ${ announcement.title }, target: ${ announcement.target })`);
|
|
136
|
+
|
|
137
|
+
await dispatch('notifications/add', notification);
|
|
138
|
+
} else {
|
|
139
|
+
logger.error(`Announcement type ${ announcement.target } is not supported`);
|
|
140
|
+
}
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"releases": [
|
|
4
|
+
{
|
|
5
|
+
"name": "2.12.2"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "2.11.3"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "2.10.3"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"announcements": [
|
|
15
|
+
{
|
|
16
|
+
"id": "security-update",
|
|
17
|
+
"target": "notification/announcement",
|
|
18
|
+
"title": "Important Security Update",
|
|
19
|
+
"message": "A critical security vulnerability has been discovered in version 2.10.1. Users are strongly advised to update to version 2.12.2 immediately to ensure their systems remain secure.",
|
|
20
|
+
"cta": {
|
|
21
|
+
"primary" : {
|
|
22
|
+
"action": "Update Now",
|
|
23
|
+
"link": "https://www.suse.com/"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "suse-rocks",
|
|
29
|
+
"target": "homepage/banner",
|
|
30
|
+
"title": "Important Security Update",
|
|
31
|
+
"message": "A critical security vulnerability has been discovered in version 2.10.1. Users are strongly advised to update to version 2.12.2 immediately to ensure their systems remain secure.",
|
|
32
|
+
"cta": {
|
|
33
|
+
"primary" : {
|
|
34
|
+
"action": "Update Now",
|
|
35
|
+
"link": "https://www.suse.com/"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -15,6 +15,7 @@ import { Context, DynamicContent, VersionInfo } from './types';
|
|
|
15
15
|
import { createLogger, LOCAL_STORAGE_CONTENT_DEBUG_LOG } from './util';
|
|
16
16
|
import { getConfig } from './config';
|
|
17
17
|
import { SystemInfoProvider } from './info';
|
|
18
|
+
import { processAnnouncements } from './announcement';
|
|
18
19
|
|
|
19
20
|
const FETCH_DELAY = 3 * 1000; // Short delay to let UI settle before we fetch the updates document
|
|
20
21
|
const FETCH_REQUEST_TIMEOUT = 15000; // Time out the request after 15 seconds
|
|
@@ -83,7 +84,7 @@ export async function fetchAndProcessDynamicContent(dispatch: Function, getters:
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
const versionInfo: VersionInfo = {
|
|
86
|
-
version,
|
|
87
|
+
version: version as semver.SemVer, // Will be defined, can not be null here
|
|
87
88
|
isPrime: config.prime,
|
|
88
89
|
};
|
|
89
90
|
|
|
@@ -103,7 +104,7 @@ export async function fetchAndProcessDynamicContent(dispatch: Function, getters:
|
|
|
103
104
|
// If the cached content has a debug version then use that as an override for the current version number
|
|
104
105
|
// This is only for debug and testing purposes
|
|
105
106
|
if (content.settings?.debugVersion) {
|
|
106
|
-
versionInfo.version = semver.coerce(content.settings.debugVersion);
|
|
107
|
+
versionInfo.version = semver.coerce(content.settings.debugVersion) || version;
|
|
107
108
|
logger.debug(`Overriding version number to ${ content.settings.debugVersion }`);
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -117,6 +118,9 @@ export async function fetchAndProcessDynamicContent(dispatch: Function, getters:
|
|
|
117
118
|
// EOM, EOL notifications
|
|
118
119
|
processSupportNotices(context, content.support, versionInfo);
|
|
119
120
|
}
|
|
121
|
+
|
|
122
|
+
// Announcements - processed for all users
|
|
123
|
+
processAnnouncements(context, content.announcements, versionInfo);
|
|
120
124
|
} catch (e) {
|
|
121
125
|
logger.error('Error reading or processing dynamic content', e);
|
|
122
126
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { SETTING } from '@shell/config/settings';
|
|
11
11
|
import { getVersionData } from '@shell/config/version';
|
|
12
12
|
import { SettingsInfo } from '@shell/utils/dynamic-content/types';
|
|
13
|
+
import { STEVE_CACHE } from '@shell/store/features';
|
|
13
14
|
|
|
14
15
|
const QS_VERSION = 'v1'; // Include a version number in the query string in case we want to version the set of params we are sending
|
|
15
16
|
const UNKNOWN = 'unknown';
|
|
@@ -26,6 +27,29 @@ const SUSE_EXTENSIONS = [
|
|
|
26
27
|
'virtual-clusters'
|
|
27
28
|
];
|
|
28
29
|
|
|
30
|
+
type FeatureFlagInfos = {
|
|
31
|
+
[id: string]: {
|
|
32
|
+
/**
|
|
33
|
+
* Query param, in format `ff-<param>`
|
|
34
|
+
*/
|
|
35
|
+
param: string,
|
|
36
|
+
/**
|
|
37
|
+
* The actual value used by the UI, roughly spec.value || status.default
|
|
38
|
+
*/
|
|
39
|
+
value: string,
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Explicit ff's to send
|
|
45
|
+
*/
|
|
46
|
+
const ffs: FeatureFlagInfos = {
|
|
47
|
+
[STEVE_CACHE]: {
|
|
48
|
+
param: 'usc',
|
|
49
|
+
value: '',
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
29
53
|
/**
|
|
30
54
|
* System information that is collected and which can then be encoded into a query string in the dyanmic content request
|
|
31
55
|
*/
|
|
@@ -47,6 +71,7 @@ type SystemInfo = {
|
|
|
47
71
|
browserSize: string;
|
|
48
72
|
screenSize: string;
|
|
49
73
|
language: string;
|
|
74
|
+
featureFlags: FeatureFlagInfos
|
|
50
75
|
};
|
|
51
76
|
|
|
52
77
|
/**
|
|
@@ -107,8 +132,7 @@ export class SystemInfoProvider {
|
|
|
107
132
|
// High-level information from clusters
|
|
108
133
|
const counts = this.getAll(getters, COUNT)?.[0]?.counts || {};
|
|
109
134
|
const clusterCount = counts[MANAGEMENT.CLUSTER] || {};
|
|
110
|
-
const
|
|
111
|
-
const localCluster = all ? all.find((c: any) => c.isLocal) : undefined;
|
|
135
|
+
const localCluster = getters['localCluster'];
|
|
112
136
|
|
|
113
137
|
// Stats for installed extensions
|
|
114
138
|
const uiExtensionList = getters['uiplugins/plugins'];
|
|
@@ -132,6 +156,19 @@ export class SystemInfoProvider {
|
|
|
132
156
|
const screenSize = `${ window.screen?.width || '?' }x${ window.screen?.height || '?' }`;
|
|
133
157
|
const browserSize = `${ window.innerWidth }x${ window.innerHeight }`;
|
|
134
158
|
|
|
159
|
+
const safeFfs = Object.entries(ffs).reduce((res, [id, ff]) => {
|
|
160
|
+
try {
|
|
161
|
+
res[id] = {
|
|
162
|
+
param: ff.param,
|
|
163
|
+
value: getters['features/get'](id),
|
|
164
|
+
};
|
|
165
|
+
} catch (e) {
|
|
166
|
+
console.debug(`Cannot include Feature Flag "${ id }" in dynamic feature request: `, e); // eslint-disable-line no-console
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return res;
|
|
170
|
+
}, {} as FeatureFlagInfos);
|
|
171
|
+
|
|
135
172
|
return {
|
|
136
173
|
systemUUID,
|
|
137
174
|
userHash,
|
|
@@ -147,6 +184,7 @@ export class SystemInfoProvider {
|
|
|
147
184
|
screenSize,
|
|
148
185
|
browserSize,
|
|
149
186
|
language: window.navigator?.language,
|
|
187
|
+
featureFlags: safeFfs,
|
|
150
188
|
};
|
|
151
189
|
}
|
|
152
190
|
|
|
@@ -214,6 +252,10 @@ export class SystemInfoProvider {
|
|
|
214
252
|
params.push(`ss=${ systemData.screenSize }`);
|
|
215
253
|
}
|
|
216
254
|
|
|
255
|
+
Object.values(systemData.featureFlags).forEach((ff) => {
|
|
256
|
+
params.push(`ff-` + `${ ff.param }=${ ff.value }`);
|
|
257
|
+
});
|
|
258
|
+
|
|
217
259
|
return params.join('&');
|
|
218
260
|
}
|
|
219
261
|
}
|
|
@@ -27,7 +27,7 @@ export async function processReleaseVersion(context: Context, releaseInfo: Relea
|
|
|
27
27
|
const versions = releaseInfo.map((v: any) => semver.coerce(v.name));
|
|
28
28
|
|
|
29
29
|
// Sort the versions, so that the newest is first in the list
|
|
30
|
-
versions.sort((a: any, b: any) => semver.
|
|
30
|
+
versions.sort((a: any, b: any) => semver.rcompare(a, b));
|
|
31
31
|
|
|
32
32
|
// Find first newer version
|
|
33
33
|
const newer = versions.find((v: any) => semver.gt(v, version));
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification handler for dynamic content announcements
|
|
3
|
+
*
|
|
4
|
+
* This provides custom handling for read/unread state using a single user preference
|
|
5
|
+
*/
|
|
6
|
+
import { Notification, NotificationHandler } from '@shell/types/notifications';
|
|
7
|
+
import { READ_ANNOUNCEMENTS } from '@shell/store/prefs';
|
|
8
|
+
import { ANNOUNCEMENT_PREFIX } from './announcement';
|
|
9
|
+
|
|
10
|
+
// Global name for this handler that can be used when creating notifications to associate them with this handler
|
|
11
|
+
export const DynamicContentAnnouncementHandlerName = 'dc-announcements';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create the dynamic content notification handler
|
|
15
|
+
*
|
|
16
|
+
* This is for announcements, where we need to manage an array of IDs of announcements that have been read, which
|
|
17
|
+
* is taken care of by this custom handler.
|
|
18
|
+
*
|
|
19
|
+
* When a notification is read/unread that specifies this handler, we will add or remove its ID from the list of
|
|
20
|
+
* read IDs that we maintain in the user preference value.
|
|
21
|
+
*
|
|
22
|
+
* This allows us to use a single user preference to track read announcements
|
|
23
|
+
*/
|
|
24
|
+
export function createHandler(store: any): NotificationHandler {
|
|
25
|
+
return {
|
|
26
|
+
async onReadUpdated(notification: Notification, read: boolean) {
|
|
27
|
+
if (!notification.id.startsWith(ANNOUNCEMENT_PREFIX)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const id = notification.id.substring(ANNOUNCEMENT_PREFIX.length);
|
|
32
|
+
const announcements = store.getters['notifications/all'].filter((n: any) => n.id.startsWith(ANNOUNCEMENT_PREFIX));
|
|
33
|
+
const pref = store.getters['prefs/get'](READ_ANNOUNCEMENTS) || '';
|
|
34
|
+
const values = !pref.length ? [] : pref.split(',').filter((v: string) => !announcements.includes(v));
|
|
35
|
+
const valuesUnique = new Set(values);
|
|
36
|
+
|
|
37
|
+
if (read) {
|
|
38
|
+
valuesUnique.add(id);
|
|
39
|
+
} else {
|
|
40
|
+
valuesUnique.delete(id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const newValues = Array.from(valuesUnique).sort();
|
|
44
|
+
|
|
45
|
+
await store.dispatch('prefs/set', { key: READ_ANNOUNCEMENTS, value: newValues.join(',') });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -56,7 +56,7 @@ export type Context = {
|
|
|
56
56
|
* Version information
|
|
57
57
|
*/
|
|
58
58
|
export type VersionInfo = {
|
|
59
|
-
version: SemVer
|
|
59
|
+
version: SemVer;
|
|
60
60
|
isPrime: boolean;
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -90,6 +90,57 @@ export type SupportInfo = {
|
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Call to action for an announcement
|
|
95
|
+
*/
|
|
96
|
+
export type CallToAction = {
|
|
97
|
+
action: string;
|
|
98
|
+
link: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Announcements to be shown in the notification center or on the home page
|
|
103
|
+
*
|
|
104
|
+
* Target determines where the notification will be shown, supported values:
|
|
105
|
+
*
|
|
106
|
+
* - `notification/announcement` - Shown with `Announcement` level in the Notification Center
|
|
107
|
+
* - `notification/info` - Shown with `Info` level in the Notification Center
|
|
108
|
+
* - `notification/warning` - Shown with `Warning` level in the Notification Center
|
|
109
|
+
* - `homepage/banner` - Shown on the home page as a banner beneath the main banner
|
|
110
|
+
* - `homepage/rhs` - Shown on the home page as a panel beneath the right-hand side links panel
|
|
111
|
+
*
|
|
112
|
+
*/
|
|
113
|
+
export type Announcement = {
|
|
114
|
+
id: string; // Unique id for this announcement
|
|
115
|
+
title: string; // Title to be shown
|
|
116
|
+
message: string; // Message/Body for the announcement
|
|
117
|
+
target: string; // Where the announcement should be shown
|
|
118
|
+
version?: string; // Version or semver expression for when to show this announcement
|
|
119
|
+
audience?: 'admin' | 'all'; // Audience - show for just Admins or for all users
|
|
120
|
+
icon?: string;
|
|
121
|
+
cta?: {
|
|
122
|
+
primary: CallToAction, // Must have a primary call to action, if we have a cta field
|
|
123
|
+
secondary?: CallToAction,
|
|
124
|
+
},
|
|
125
|
+
style?: string; // Styling information that will be interpreted by the rendering component
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Icon information
|
|
130
|
+
*/
|
|
131
|
+
export type AnnouncementNotificationIconData = {
|
|
132
|
+
light: string; // Light mode icon/image
|
|
133
|
+
dark?: string; // Light mode icon/image
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Custom data for announcements stored with the notification
|
|
138
|
+
*/
|
|
139
|
+
export type AnnouncementNotificationData = {
|
|
140
|
+
icon?: AnnouncementNotificationIconData; // Icon/Image to show
|
|
141
|
+
location: string; // Location of the announcement in the UI
|
|
142
|
+
};
|
|
143
|
+
|
|
93
144
|
/**
|
|
94
145
|
* Main type for the metadata that is retrieved from the dynamic content endpoint
|
|
95
146
|
*/
|
|
@@ -97,5 +148,6 @@ export type DynamicContent = {
|
|
|
97
148
|
version: string;
|
|
98
149
|
releases: ReleaseInfo[],
|
|
99
150
|
support: SupportInfo,
|
|
151
|
+
announcements: Announcement[],
|
|
100
152
|
settings?: Partial<SettingsInfo>,
|
|
101
153
|
};
|
|
@@ -81,7 +81,7 @@ export function importWindowComponent(name) {
|
|
|
81
81
|
throw new Error('Name required');
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
return defineAsyncComponent(() => import(/* webpackChunkName: "components
|
|
84
|
+
return defineAsyncComponent(() => import(/* webpackChunkName: "components" */ `@shell/components/Window/${name}`));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export function loadProduct(name) {
|
|
@@ -130,7 +130,7 @@ export function resolveDetail(key) {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
export function resolveWindowComponent(key) {
|
|
133
|
-
return require.resolve(`@shell/components/
|
|
133
|
+
return require.resolve(`@shell/components/Window/${ key }`);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
export function resolveMachineConfigComponent(key) {
|
package/utils/favicon.js
CHANGED
|
@@ -9,17 +9,17 @@ export function haveSetFavIcon() {
|
|
|
9
9
|
|
|
10
10
|
export function setFavIcon(store) {
|
|
11
11
|
const res = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.FAVICON);
|
|
12
|
-
const brandSetting = store.getters['management/
|
|
12
|
+
const brandSetting = store.getters['management/brand'];
|
|
13
13
|
const link = findIconLink(document.head.getElementsByTagName('link'));
|
|
14
14
|
|
|
15
15
|
if (link) {
|
|
16
16
|
let brandImage;
|
|
17
17
|
|
|
18
|
-
if (brandSetting
|
|
18
|
+
if (brandSetting === 'suse') {
|
|
19
19
|
brandImage = require('~shell/assets/brand/suse/favicon.png');
|
|
20
|
-
} else if (brandSetting
|
|
20
|
+
} else if (brandSetting === 'csp') {
|
|
21
21
|
brandImage = require('~shell/assets/brand/csp/favicon.png');
|
|
22
|
-
} else if (brandSetting
|
|
22
|
+
} else if (brandSetting === 'harvester') {
|
|
23
23
|
brandImage = require('~shell/assets/brand/harvester/favicon.png');
|
|
24
24
|
}
|
|
25
25
|
|
package/utils/object.js
CHANGED
|
@@ -205,7 +205,7 @@ export function definedKeys(obj) {
|
|
|
205
205
|
return compact(flattenDeep(keys));
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
export function diff(from, to) {
|
|
208
|
+
export function diff(from, to, preventNull = false) {
|
|
209
209
|
from = from || {};
|
|
210
210
|
to = to || {};
|
|
211
211
|
|
|
@@ -234,7 +234,25 @@ export function diff(from, to) {
|
|
|
234
234
|
const missing = difference(fromKeys, toKeys);
|
|
235
235
|
|
|
236
236
|
for ( const k of missing ) {
|
|
237
|
-
|
|
237
|
+
// we need to gate this in order to prevent unforseen problems with addonConfig
|
|
238
|
+
if (preventNull) {
|
|
239
|
+
// keys that come from "definedKeys" method are strings with "" chars inside... We need to clean them up
|
|
240
|
+
// so that we can access the value of the obj property
|
|
241
|
+
let key = k;
|
|
242
|
+
|
|
243
|
+
if (!k.includes('.')) {
|
|
244
|
+
key = k.replaceAll('"', '');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// // if value exists in "from" but is missing in "to", let's add it, otherwise we just set it null as we did before
|
|
248
|
+
if (from[key] !== undefined && from[key] !== null) {
|
|
249
|
+
set(out, key, from[key]);
|
|
250
|
+
} else {
|
|
251
|
+
set(out, key, null);
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
set(out, k, null);
|
|
255
|
+
}
|
|
238
256
|
}
|
|
239
257
|
|
|
240
258
|
return out;
|
|
@@ -165,7 +165,7 @@ class PaginationUtils {
|
|
|
165
165
|
/**
|
|
166
166
|
* Is pagination enabled at a global level or for a specific resource
|
|
167
167
|
*/
|
|
168
|
-
isEnabled({ rootGetters, $
|
|
168
|
+
isEnabled({ rootGetters, $extension }: any, enabledFor: PaginationResourceContext) {
|
|
169
169
|
// Cache must be enabled to support pagination api
|
|
170
170
|
if (!this.isSteveCacheEnabled({ rootGetters })) {
|
|
171
171
|
return false;
|
|
@@ -184,7 +184,7 @@ class PaginationUtils {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// Does an extension say this type is enabled?
|
|
187
|
-
const plugin = $
|
|
187
|
+
const plugin = $extension as ExtensionManager;
|
|
188
188
|
const paginationExtensionPoints = plugin.getAll()[EXT_IDS.SERVER_SIDE_PAGINATION_RESOURCES];
|
|
189
189
|
|
|
190
190
|
if (paginationExtensionPoints) {
|
|
@@ -19,7 +19,7 @@ interface Args {
|
|
|
19
19
|
/**
|
|
20
20
|
* Callback called when the resource is changed (notified by socket)
|
|
21
21
|
*/
|
|
22
|
-
onChange?:
|
|
22
|
+
onChange?: STEVE_WATCH_EVENT_LISTENER_CALLBACK,
|
|
23
23
|
|
|
24
24
|
formatResponse?: {
|
|
25
25
|
/**
|
|
@@ -69,16 +69,16 @@ class PaginationWrapper<T extends object> {
|
|
|
69
69
|
this.classify = formatResponse?.classify || false;
|
|
70
70
|
this.reactive = formatResponse?.reactive || false;
|
|
71
71
|
|
|
72
|
-
this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $
|
|
72
|
+
this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $extension: this.$store.$extension }, enabledFor);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
async request(
|
|
76
|
-
|
|
75
|
+
async request({ pagination, forceWatch }: {
|
|
76
|
+
forceWatch?: boolean,
|
|
77
|
+
pagination: PaginationArgs,
|
|
77
78
|
}): Promise<Result<T>> {
|
|
78
79
|
if (!this.isEnabled) {
|
|
79
80
|
throw new Error(`Wrapper for type '${ this.enabledFor.store }/${ this.enabledFor.resource?.id }' in context '${ this.enabledFor.resource?.context }' not supported`);
|
|
80
81
|
}
|
|
81
|
-
const { pagination } = args;
|
|
82
82
|
const opt: ActionFindPageArgs = {
|
|
83
83
|
watch: false,
|
|
84
84
|
pagination,
|
|
@@ -89,14 +89,18 @@ class PaginationWrapper<T extends object> {
|
|
|
89
89
|
const out: ActionFindPageTransientResult<T> = await this.$store.dispatch(`${ this.enabledFor.store }/findPage`, { opt, type: this.enabledFor.resource?.id });
|
|
90
90
|
|
|
91
91
|
// Watch
|
|
92
|
-
|
|
92
|
+
const firstTime = !this.steveWatchParams;
|
|
93
|
+
|
|
94
|
+
if (this.onChange && (firstTime || forceWatch) ) { // && !this.steveWatchParams
|
|
93
95
|
this.steveWatchParams = {
|
|
94
96
|
event: STEVE_WATCH_EVENT_TYPES.CHANGES,
|
|
95
97
|
id: this.id,
|
|
96
98
|
params: {
|
|
97
|
-
type:
|
|
98
|
-
mode:
|
|
99
|
-
|
|
99
|
+
type: this.enabledFor.resource?.id as string,
|
|
100
|
+
mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
|
|
101
|
+
force: forceWatch,
|
|
102
|
+
},
|
|
103
|
+
|
|
100
104
|
};
|
|
101
105
|
|
|
102
106
|
this.watch();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IClusterProvisioner, ClusterProvisionerContext } from '@shell/core/types';
|
|
2
|
+
|
|
3
|
+
export function getHostedProviders(context: ClusterProvisionerContext) {
|
|
4
|
+
return context?.$extension?.getProviders(context)?.filter((p: IClusterProvisioner) => p.group === 'hosted') || [];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isHostedProvider(context: ClusterProvisionerContext, provisioner: string) {
|
|
8
|
+
if (!provisioner) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const provisioners = new Set(getHostedProviders(context).map((p: IClusterProvisioner) => p.id.toLowerCase()));
|
|
12
|
+
|
|
13
|
+
return provisioners.has(provisioner.toLowerCase());
|
|
14
|
+
}
|
package/utils/scroll.js
ADDED
package/utils/selector-typed.ts
CHANGED
|
@@ -199,8 +199,12 @@ export function labelSelectorToSelector(labelSelector?: KubeLabelSelector): stri
|
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
(labelSelector?.matchExpressions || []).forEach((value: KubeLabelSelectorExpression) => {
|
|
202
|
-
if (value.operator === 'In' && value.values
|
|
203
|
-
|
|
202
|
+
if (value.operator === 'In' && value.values !== undefined) {
|
|
203
|
+
if (value.values?.length === 1) {
|
|
204
|
+
res.push(`${ value.key }=${ value.values[0] }`);
|
|
205
|
+
} else {
|
|
206
|
+
res.push(`${ value.key } in (${ value.values.join(',') })`);
|
|
207
|
+
}
|
|
204
208
|
} else {
|
|
205
209
|
throw new Error(`Unsupported matchExpression found when converting to selector string. ${ value }`);
|
|
206
210
|
}
|
package/utils/settings.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Store } from 'vuex';
|
|
|
3
3
|
import { DEFAULT_PERF_SETTING, PerfSettings, SETTING } from '@shell/config/settings';
|
|
4
4
|
import { pluralize } from '@shell/utils/string';
|
|
5
5
|
import { _MULTI } from '@shell/plugins/dashboard-store/actions';
|
|
6
|
+
import { ClusterProvisionerContext } from '@shell/core/types';
|
|
6
7
|
|
|
7
8
|
export const fetchOrCreateSetting = async(store: Store<any>, id: string, val: string, save = true): Promise<any> => {
|
|
8
9
|
let setting;
|
|
@@ -109,3 +110,17 @@ export const getPerformanceSetting = (rootGetters: Record<string, (arg0: string,
|
|
|
109
110
|
|
|
110
111
|
return Object.assign(safeDefaults, perfSetting || {});
|
|
111
112
|
};
|
|
113
|
+
|
|
114
|
+
export const isProviderEnabled = (context: ClusterProvisionerContext, provider: string): boolean => {
|
|
115
|
+
const providerTypesJSON = context.getters['management/byId'](MANAGEMENT.SETTING, SETTING.KEV2_OPERATORS )?.value;
|
|
116
|
+
const providerTypes = providerTypesJSON ? JSON.parse(providerTypesJSON) : [];
|
|
117
|
+
|
|
118
|
+
for ( let i = 0; i < providerTypes.length; i++) {
|
|
119
|
+
if ( providerTypes[i].name === provider) {
|
|
120
|
+
return providerTypes[i].active;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// We want to have providers enabled by default unless they are turned off by a setting
|
|
125
|
+
return true;
|
|
126
|
+
};
|