Skip to content

Commit

Permalink
Merge pull request #130417 from dhartunian/backport24.2-129420
Browse files Browse the repository at this point in the history
release-24.2: ui: add license change notification to db console
  • Loading branch information
dhartunian authored Sep 12, 2024
2 parents fda52d9 + 49a843d commit 3424fc9
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 15 deletions.
27 changes: 27 additions & 0 deletions pkg/ui/workspaces/db-console/src/redux/alerts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Long from "long";
import * as protos from "src/js/protos";
import { cockroach } from "src/js/protos";
import { API_PREFIX } from "src/util/api";
import { setDataFromServer } from "src/util/dataFromServer";
import fetchMock from "src/util/fetch-mock";
import { versionsSelector } from "src/redux/nodes";

Expand All @@ -33,10 +34,12 @@ import {
emailSubscriptionAlertSelector,
clusterPreserveDowngradeOptionDismissedSetting,
clusterPreserveDowngradeOptionOvertimeSelector,
licenseUpdateNotificationSelector,
} from "./alerts";
import {
VERSION_DISMISSED_KEY,
INSTRUCTIONS_BOX_COLLAPSED_KEY,
LICENSE_UPDATE_DISMISSED_KEY,
setUIDataKey,
isInFlight,
} from "./uiData";
Expand Down Expand Up @@ -258,6 +261,29 @@ describe("alerts", function () {
});
});

describe("licence update notification", function () {
it("displays the alert when nothing is done", function () {
dispatch(setUIDataKey(LICENSE_UPDATE_DISMISSED_KEY, null));
const alert = licenseUpdateNotificationSelector(state());
expect(typeof alert).toBe("object");
expect(alert.level).toEqual(AlertLevel.INFORMATION);
expect(alert.text).toEqual("Important changes to CockroachDB’s licensing model.");
})

it("hides the alert when dismissed timestamp is present", function () {
dispatch(setUIDataKey(LICENSE_UPDATE_DISMISSED_KEY, moment()));
expect(licenseUpdateNotificationSelector(state())).toBeUndefined();
})

it("hides the alert when license is enterprise", function () {
dispatch(setUIDataKey(LICENSE_UPDATE_DISMISSED_KEY, null));
setDataFromServer({
LicenseType: "Enterprise",
} as any)
expect(licenseUpdateNotificationSelector(state())).toBeUndefined();
})
})

describe("new version available notification", function () {
it("displays nothing when versions have not yet been loaded", function () {
dispatch(setUIDataKey(VERSION_DISMISSED_KEY, null));
Expand Down Expand Up @@ -631,6 +657,7 @@ describe("alerts", function () {
);
dispatch(setUIDataKey(VERSION_DISMISSED_KEY, "blank"));
dispatch(setUIDataKey(INSTRUCTIONS_BOX_COLLAPSED_KEY, false));
dispatch(setUIDataKey(LICENSE_UPDATE_DISMISSED_KEY, moment()));
dispatch(
versionReducerObj.receiveData({
details: [],
Expand Down
115 changes: 101 additions & 14 deletions pkg/ui/workspaces/db-console/src/redux/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { getDataFromServer } from "../util/dataFromServer";

import { LocalSetting } from "./localsettings";
import {
LICENSE_UPDATE_DISMISSED_KEY,
VERSION_DISMISSED_KEY,
INSTRUCTIONS_BOX_COLLAPSED_KEY,
saveUIData,
Expand All @@ -61,6 +62,7 @@ export enum AlertLevel {
WARNING,
CRITICAL,
SUCCESS,
INFORMATION,
}

export interface AlertInfo {
Expand Down Expand Up @@ -635,20 +637,6 @@ export const upgradeNotFinalizedWarningSelector = createSelector(
},
);

/**
* Selector which returns an array of all active alerts which should be
* displayed in the overview list page, these should be non-critical alerts.
*/

export const overviewListAlertsSelector = createSelector(
staggeredVersionWarningSelector,
clusterPreserveDowngradeOptionOvertimeSelector,
upgradeNotFinalizedWarningSelector,
(...alerts: Alert[]): Alert[] => {
return without(alerts, null, undefined);
},
);

/**
* Selector which returns an array of all active alerts which should be
* displayed in the alerts panel, which is embedded within the cluster overview
Expand Down Expand Up @@ -707,6 +695,104 @@ export const licenseTypeSelector = createSelector(
data => licenseTypeNames.get(data.LicenseType) || "None",
);

export const licenseUpdateDismissedLocalSetting = new LocalSetting(
"license_update_dismissed",
localSettingsSelector,
moment(0),
);

const licenseUpdateDismissedPersistentLoadedSelector = createSelector(
(state: AdminUIState) => state.uiData,
uiData => uiData && uiData.hasOwnProperty(LICENSE_UPDATE_DISMISSED_KEY),
);

const licenseUpdateDismissedPersistentSelector = createSelector(
(state: AdminUIState) => state.uiData,
uiData => moment(uiData?.[LICENSE_UPDATE_DISMISSED_KEY]?.data ?? 0),
);

export const licenseUpdateNotificationSelector = createSelector(
licenseTypeSelector,
licenseUpdateDismissedLocalSetting.selector,
licenseUpdateDismissedPersistentSelector,
licenseUpdateDismissedPersistentLoadedSelector,
(
licenseType,
licenseUpdateDismissed,
licenseUpdateDismissedPersistent,
licenseUpdateDismissedPersistentLoaded,
): Alert => {
// If customer has Enterprise license they don't need to worry about this.
if (licenseType === "Enterprise") {
return undefined;
}

// If the notification has been dismissed based on the session storage
// timestamp, don't show it.'
//
// Note: `licenseUpdateDismissed` is wrapped in `moment()` because
// the local storage selector won't convert it back from a string.
// We omit fixing that here since this change is being backported
// to many versions.
if (moment(licenseUpdateDismissed).isAfter(moment(0))) {
return undefined;
}

// If the notification has been dismissed based on the uiData
// storage in the cluster, don't show it. Note that this is
// different from how version upgrade notifications work, this one
// is dismissed forever and won't return even if you upgrade
// further or time passes.
if (
licenseUpdateDismissedPersistentLoaded &&
licenseUpdateDismissedPersistent &&
licenseUpdateDismissedPersistent.isAfter(moment(0))
) {
return undefined;
}

return {
level: AlertLevel.INFORMATION,
title: "Coming November 18, 2024",
text: "Important changes to CockroachDB’s licensing model.",
link: docsURL.enterpriseLicenseUpdate,
dismiss: (dispatch: any) => {
const dismissedAt = moment();
// Note(davidh): I haven't been able to find historical context
// for why some alerts have both a "local" and a "persistent"
// dismissal. My thinking is that just the persistent dismissal
// should be adequate, but I'm preserving that behavior here to
// match the version upgrade notification.

// Dismiss locally.
dispatch(licenseUpdateDismissedLocalSetting.set(dismissedAt));
// Dismiss persistently.
return dispatch(
saveUIData({
key: LICENSE_UPDATE_DISMISSED_KEY,
value: dismissedAt.valueOf(),
})
);
},
};
},
);

/**
* Selector which returns an array of all active alerts which should be
* displayed in the overview list page, these should be non-critical alerts.
*/

export const overviewListAlertsSelector = createSelector(
staggeredVersionWarningSelector,
clusterPreserveDowngradeOptionOvertimeSelector,
upgradeNotFinalizedWarningSelector,
licenseUpdateNotificationSelector,
(...alerts: Alert[]): Alert[] => {
return without(alerts, null, undefined);
},
);

// daysUntilLicenseExpiresSelector returns number of days remaining before license expires.
export const daysUntilLicenseExpiresSelector = createSelector(
getDataFromServer,
Expand Down Expand Up @@ -780,6 +866,7 @@ export function alertDataSync(store: Store<AdminUIState>) {
const keysToMaybeLoad = [
VERSION_DISMISSED_KEY,
INSTRUCTIONS_BOX_COLLAPSED_KEY,
LICENSE_UPDATE_DISMISSED_KEY,
];
const keysToLoad = filter(keysToMaybeLoad, key => {
return !(has(uiData, key) || isInFlight(state, key));
Expand Down
5 changes: 5 additions & 0 deletions pkg/ui/workspaces/db-console/src/redux/uiData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ export class OptInAttributes {
// was last dismissed.
export const VERSION_DISMISSED_KEY = "version_dismissed";

// LICENSE_UPDATE_DISMISSED_KEY is the uiData key on the server that tracks when the licence
// update banner was last dismissed. This banner notifies the user that we've changed our
// licensing if they're deployed without an active license.
export const LICENSE_UPDATE_DISMISSED_KEY = "license_update_dismissed";

// INSTRUCTIONS_BOX_COLLAPSED_KEY is the uiData key on the server that tracks whether the
// instructions box on the cluster viz has been collapsed or not.
export const INSTRUCTIONS_BOX_COLLAPSED_KEY =
Expand Down
1 change: 1 addition & 0 deletions pkg/ui/workspaces/db-console/src/util/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export let upgradeTroubleshooting: string;
export let licensingFaqs: string;
// Note that these explicitly don't use the current version, since we want to
// link to the most up-to-date documentation available.
export const enterpriseLicenseUpdate = "https://www.cockroachlabs.com/enterprise-license-update/"
export const upgradeCockroachVersion =
"https://www.cockroachlabs.com/docs/stable/upgrade-cockroach-version.html";
export const enterpriseLicensing =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
border-color $notification-info-border-color
background-color $notification-info-fill-color

&--information
color $body-color
border-color $notification-info-border-color
background-color $notification-info-fill-color
path
fill $colors--primary-blue-3

&--warning
color $body-color
border-color $notification-warning-border-color
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
warningIcon,
notificationIcon,
criticalIcon,
informationIcon,
} from "src/views/shared/components/icons";
import { trustIcon } from "src/util/trust";

Expand All @@ -27,6 +28,8 @@ function alertIcon(level: AlertLevel) {
return trustIcon(criticalIcon);
case AlertLevel.WARNING:
return trustIcon(warningIcon);
case AlertLevel.INFORMATION:
return trustIcon(informationIcon);
default:
return trustIcon(notificationIcon);
}
Expand All @@ -49,7 +52,7 @@ export class AlertBox extends React.Component<AlertBoxProps, {}> {

const learnMore = this.props.link && (
<a className="" href={this.props.link}>
Learn More.
Learn More
</a>
);
content = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ export const warningIcon = `<svg width="21px" height="21px" viewBox="0 0 21 21"
</g>
</svg>`;

export const informationIcon = `
<svg width="24" height="24" viewBox="0 0 24 24" fill="" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_11029_22094)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 12C0 5.37261 5.39028 0 12 0C18.6274 0 24 5.37261 24 12C24 18.6274 18.6274 24 12 24C5.37261 24 0 18.6274 0 12ZM11.1163 18.7865H12.8836V9.52586H11.1163V18.7865ZM11.1163 7.68786H12.8836V5.21364H11.1163V7.68786Z" fill="#394455"/>
</g>
<defs>
<clipPath id="clip0_11029_22094">
<rect width="24" height="24" fill="white" transform="matrix(1 0 0 -1 0 24)"/>
</clipPath>
</defs>
</svg>
`

export const notificationIcon = `
<svg width="22px" height="22px" viewBox="0 0 38 38" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.3.3 (12081) - http://www.bohemiancoding.com/sketch -->
Expand Down

0 comments on commit 3424fc9

Please sign in to comment.