Skip to content

Commit

Permalink
Merge pull request #130426 from dhartunian/backport24.1-129420
Browse files Browse the repository at this point in the history
release-24.1: ui: add license change notification to db console
  • Loading branch information
dhartunian committed Sep 12, 2024
2 parents 66c7ee8 + fe0aa3a commit aaa53b2
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 15 deletions.
29 changes: 29 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 @@ -15,6 +15,7 @@ import { createHashHistory } from "history";
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 { AdminUIState, AppDispatch, createAdminUIStore } from "./state";
Expand All @@ -31,11 +32,13 @@ import {
emailSubscriptionAlertSelector,
clusterPreserveDowngradeOptionDismissedSetting,
clusterPreserveDowngradeOptionOvertimeSelector,
licenseUpdateNotificationSelector,
} from "./alerts";
import { versionsSelector } from "src/redux/nodes";
import {
VERSION_DISMISSED_KEY,
INSTRUCTIONS_BOX_COLLAPSED_KEY,
LICENSE_UPDATE_DISMISSED_KEY,
setUIDataKey,
isInFlight,
} from "./uiData";
Expand Down Expand Up @@ -257,6 +260,31 @@ 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 @@ -630,6 +658,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
117 changes: 103 additions & 14 deletions pkg/ui/workspaces/db-console/src/redux/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ThunkAction } from "redux-thunk";

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

export interface AlertInfo {
Expand Down Expand Up @@ -629,20 +631,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 @@ -701,6 +689,106 @@ 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 &&
Object.prototype.hasOwnProperty.call(uiData, 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 @@ -774,6 +862,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 @@ -56,6 +56,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
2 changes: 2 additions & 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,8 @@ 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 @@ -18,6 +18,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: string = `<svg width="21px" height="21px" viewBox="0 0
</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: string = `
<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 aaa53b2

Please sign in to comment.