diff --git a/coderd/entitlements/entitlements.go b/coderd/entitlements/entitlements.go index b57135e984b8c..e141a861a9045 100644 --- a/coderd/entitlements/entitlements.go +++ b/coderd/entitlements/entitlements.go @@ -30,8 +30,8 @@ func New() *Set { // These will be updated when coderd is initialized. entitlements: codersdk.Entitlements{ Features: map[codersdk.FeatureName]codersdk.Feature{}, - Warnings: nil, - Errors: nil, + Warnings: []string{}, + Errors: []string{}, HasLicense: false, Trial: false, RequireTelemetry: false, @@ -39,13 +39,21 @@ func New() *Set { }, right2Update: make(chan struct{}, 1), } + // Ensure all features are present in the entitlements. Our frontend + // expects this. + for _, featureName := range codersdk.FeatureNames { + s.entitlements.AddFeature(featureName, codersdk.Feature{ + Entitlement: codersdk.EntitlementNotEntitled, + Enabled: false, + }) + } s.right2Update <- struct{}{} // one token, serialized updates return s } // ErrLicenseRequiresTelemetry is an error returned by a fetch passed to Update to indicate that the // fetched license cannot be used because it requires telemetry. -var ErrLicenseRequiresTelemetry = xerrors.New("License requires telemetry but telemetry is disabled") +var ErrLicenseRequiresTelemetry = xerrors.New(codersdk.LicenseTelemetryRequiredErrorText) func (l *Set) Update(ctx context.Context, fetch func(context.Context) (codersdk.Entitlements, error)) error { select { diff --git a/codersdk/licenses.go b/codersdk/licenses.go index d7634c72bf4ff..4863aad60c6ff 100644 --- a/codersdk/licenses.go +++ b/codersdk/licenses.go @@ -12,7 +12,8 @@ import ( ) const ( - LicenseExpiryClaim = "license_expires" + LicenseExpiryClaim = "license_expires" + LicenseTelemetryRequiredErrorText = "License requires telemetry but telemetry is disabled" ) type AddLicenseRequest struct { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 747459ea4efb9..d335cce7732f2 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1116,6 +1116,10 @@ export interface License { // From codersdk/licenses.go export const LicenseExpiryClaim = "license_expires"; +// From codersdk/licenses.go +export const LicenseTelemetryRequiredErrorText = + "License requires telemetry but telemetry is disabled"; + // From codersdk/deployment.go export interface LinkConfig { readonly name: string; diff --git a/site/src/modules/dashboard/LicenseBanner/LicenseBannerView.tsx b/site/src/modules/dashboard/LicenseBanner/LicenseBannerView.tsx index 8ebcb216f51cd..7803f1dc828b1 100644 --- a/site/src/modules/dashboard/LicenseBanner/LicenseBannerView.tsx +++ b/site/src/modules/dashboard/LicenseBanner/LicenseBannerView.tsx @@ -6,6 +6,7 @@ import { useTheme, } from "@emotion/react"; import Link from "@mui/material/Link"; +import { LicenseTelemetryRequiredErrorText } from "api/typesGenerated"; import { Expander } from "components/Expander/Expander"; import { Pill } from "components/Pill/Pill"; import { type FC, useState } from "react"; @@ -14,6 +15,7 @@ export const Language = { licenseIssue: "License Issue", licenseIssues: (num: number): string => `${num} License Issues`, upgrade: "Contact sales@coder.com.", + exception: "Contact sales@coder.com if you need an exception.", exceeded: "It looks like you've exceeded some limits of your license.", lessDetails: "Less", moreDetails: "More", @@ -26,6 +28,14 @@ const styles = { }, } satisfies Record>; +const formatMessage = (message: string) => { + // If the message ends with an alphanumeric character, add a period. + if (/[a-z0-9]$/i.test(message)) { + return `${message}.`; + } + return message; +}; + export interface LicenseBannerViewProps { errors: readonly string[]; warnings: readonly string[]; @@ -57,14 +67,16 @@ export const LicenseBannerView: FC = ({
{Language.licenseIssue}
- {messages[0]} + {formatMessage(messages[0])}   - {Language.upgrade} + {messages[0] === LicenseTelemetryRequiredErrorText + ? Language.exception + : Language.upgrade}
@@ -90,7 +102,7 @@ export const LicenseBannerView: FC = ({
    {messages.map((message) => (
  • - {message} + {formatMessage(message)}
  • ))}