mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
[MM-70130] Add permanent banner for non-production developer license keys (#37846)
* [MM-70130] Add permanent banner for non-production developer license keys Co-authored-by: nick.misasi <[email protected]> * [MM-70130] Update non-production license banner text Co-authored-by: Nick Misasi <[email protected]> --------- Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
Hanzo Dev
parent
32ca2cb48d
commit
3e6e5ad2f4
@@ -973,6 +973,9 @@
|
||||
is_gov_sku:
|
||||
type: boolean
|
||||
description: Whether this is a government SKU license
|
||||
is_non_production:
|
||||
type: boolean
|
||||
description: Whether the license is a non-production (developer) key
|
||||
customer:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -412,6 +412,7 @@ func (ps *PlatformService) logLicense(message string, license *model.License) {
|
||||
mlog.String("sku_short_name", license.SkuShortName),
|
||||
mlog.Bool("is_trial", license.IsTrial),
|
||||
mlog.Bool("is_gov_sku", license.IsGovSku),
|
||||
mlog.Bool("is_non_production", license.IsNonProduction),
|
||||
)
|
||||
|
||||
if license.Customer != nil {
|
||||
|
||||
@@ -152,6 +152,7 @@ func (ps *PlatformService) getSupportPacketDiagnostics(rctx request.CTX) (*model
|
||||
d.License.SkuShortName = license.SkuShortName
|
||||
d.License.IsTrial = license.IsTrial
|
||||
d.License.IsGovSKU = license.IsGovSku
|
||||
d.License.IsNonProduction = license.IsNonProduction
|
||||
}
|
||||
|
||||
/* Server */
|
||||
|
||||
@@ -264,6 +264,7 @@ func GetClientLicense(l *model.License) map[string]string {
|
||||
props["OutgoingOAuthConnections"] = strconv.FormatBool(*l.Features.OutgoingOAuthConnections)
|
||||
props["IsTrial"] = strconv.FormatBool(l.IsTrial)
|
||||
props["IsGovSku"] = strconv.FormatBool(l.IsGovSku)
|
||||
props["IsNonProduction"] = strconv.FormatBool(l.IsNonProduction)
|
||||
}
|
||||
|
||||
return props
|
||||
|
||||
@@ -253,6 +253,25 @@ func TestGetLicenseFileLocation(t *testing.T) {
|
||||
require.Equal(t, fileName, "mattermost.mattermost-license", "invalid file name")
|
||||
}
|
||||
|
||||
func TestGetClientLicense(t *testing.T) {
|
||||
license := &model.License{
|
||||
Customer: &model.Customer{},
|
||||
Features: &model.Features{},
|
||||
}
|
||||
license.Features.SetDefaults()
|
||||
|
||||
props := GetClientLicense(license)
|
||||
require.Equal(t, "false", props["IsNonProduction"])
|
||||
|
||||
license.IsNonProduction = true
|
||||
props = GetClientLicense(license)
|
||||
require.Equal(t, "true", props["IsNonProduction"])
|
||||
|
||||
// The flag must survive sanitization so all users can see the non-production banner.
|
||||
sanitized := GetSanitizedClientLicense(props)
|
||||
require.Equal(t, "true", sanitized["IsNonProduction"])
|
||||
}
|
||||
|
||||
func TestGetLicenseFileFromDisk(t *testing.T) {
|
||||
t.Run("missing file", func(t *testing.T) {
|
||||
fileBytes := GetLicenseFileFromDisk("thisfileshouldnotexist.mattermost-license")
|
||||
|
||||
@@ -86,6 +86,7 @@ type License struct {
|
||||
SkuShortName string `json:"sku_short_name"`
|
||||
IsTrial bool `json:"is_trial"`
|
||||
IsGovSku bool `json:"is_gov_sku"`
|
||||
IsNonProduction bool `json:"is_non_production"`
|
||||
IsSeatCountEnforced bool `json:"is_seat_count_enforced"`
|
||||
// ExtraUsers provides a grace mechanism that allows a configurable number of users
|
||||
// beyond the base license limit before restricting user creation. When nil, defaults to 0.
|
||||
|
||||
@@ -14,11 +14,12 @@ type SupportPacketDiagnostics struct {
|
||||
Version int `yaml:"version"`
|
||||
|
||||
License struct {
|
||||
Company string `yaml:"company"`
|
||||
Users int `yaml:"users"`
|
||||
SkuShortName string `yaml:"sku_short_name"`
|
||||
IsTrial bool `yaml:"is_trial,omitempty"`
|
||||
IsGovSKU bool `yaml:"is_gov_sku,omitempty"`
|
||||
Company string `yaml:"company"`
|
||||
Users int `yaml:"users"`
|
||||
SkuShortName string `yaml:"sku_short_name"`
|
||||
IsTrial bool `yaml:"is_trial,omitempty"`
|
||||
IsGovSKU bool `yaml:"is_gov_sku,omitempty"`
|
||||
IsNonProduction bool `yaml:"is_non_production,omitempty"`
|
||||
} `yaml:"license"`
|
||||
|
||||
Server struct {
|
||||
|
||||
@@ -12,6 +12,7 @@ import CloudTrialAnnouncementBar from './cloud_trial_announcement_bar';
|
||||
import CloudTrialEndAnnouncementBar from './cloud_trial_ended_announcement_bar';
|
||||
import ConfigurationAnnouncementBar from './configuration_bar';
|
||||
import AnnouncementBar from './default_announcement_bar';
|
||||
import NonProductionLicenseAnnouncementBar from './non_production_license_bar';
|
||||
import NotificationPermissionBar from './notification_permission_bar';
|
||||
import OverageUsersBanner from './overage_users_banner';
|
||||
import PaymentAnnouncementBar from './payment_announcement_bar';
|
||||
@@ -105,6 +106,8 @@ class AnnouncementBarController extends React.PureComponent<Props> {
|
||||
// If set with class 'admin-announcement', they will always be visible, stacked vertically.
|
||||
return (
|
||||
<>
|
||||
{/* Lowest priority: urgent bars temporarily override it, and it reappears once they clear. */}
|
||||
<NonProductionLicenseAnnouncementBar/>
|
||||
<NotificationPermissionBar/>
|
||||
{adminConfiguredAnnouncementBar}
|
||||
{errorBar}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import {AlertOutlineIcon} from '@mattermost/compass-icons/components';
|
||||
|
||||
import {getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import {AnnouncementBarTypes} from 'utils/constants';
|
||||
|
||||
import AnnouncementBar from '../default_announcement_bar';
|
||||
|
||||
const NonProductionLicenseAnnouncementBar: React.FC = () => {
|
||||
const license = useSelector(getLicense);
|
||||
|
||||
if (license?.IsNonProduction !== 'true') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<AnnouncementBar
|
||||
type={AnnouncementBarTypes.ADVISOR}
|
||||
showCloseButton={false}
|
||||
message={
|
||||
<FormattedMessage
|
||||
id='announcement_bar.non_production_license.message'
|
||||
defaultMessage='Non-production license. Test or staging use only.'
|
||||
/>
|
||||
}
|
||||
icon={<AlertOutlineIcon size={16}/>}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default NonProductionLicenseAnnouncementBar;
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithContext} from 'tests/react_testing_utils';
|
||||
|
||||
import NonProductionLicenseAnnouncementBar from './index';
|
||||
|
||||
describe('components/announcement_bar/NonProductionLicenseAnnouncementBar', () => {
|
||||
const initialState = {
|
||||
entities: {
|
||||
general: {
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
IsNonProduction: 'true',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should show banner when license is non-production', () => {
|
||||
const {container} = renderWithContext(
|
||||
<NonProductionLicenseAnnouncementBar/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.announcement-bar')).not.toBeNull();
|
||||
expect(container.textContent).toContain('Non-production license. Test or staging use only.');
|
||||
});
|
||||
|
||||
it('should not show banner when license is not non-production', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
state.entities.general.license.IsNonProduction = 'false';
|
||||
|
||||
const {container} = renderWithContext(
|
||||
<NonProductionLicenseAnnouncementBar/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.announcement-bar')).toBeNull();
|
||||
});
|
||||
|
||||
it('should not show banner when the flag is absent from the license', () => {
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
delete state.entities.general.license.IsNonProduction;
|
||||
|
||||
const {container} = renderWithContext(
|
||||
<NonProductionLicenseAnnouncementBar/>,
|
||||
state,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.announcement-bar')).toBeNull();
|
||||
});
|
||||
|
||||
it('should not be dismissable', () => {
|
||||
const {container} = renderWithContext(
|
||||
<NonProductionLicenseAnnouncementBar/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.announcement-bar__close')).toBeNull();
|
||||
});
|
||||
|
||||
it('should have advisor type', () => {
|
||||
const {container} = renderWithContext(
|
||||
<NonProductionLicenseAnnouncementBar/>,
|
||||
initialState,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.announcement-bar.announcement-bar-advisor')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -4027,6 +4027,7 @@
|
||||
"announcement_bar.error.trial_license_expiring": "There are {days} days left on your free trial.",
|
||||
"announcement_bar.error.trial_license_expiring_last_day": "This is the last day of your free trial. Purchase a license now to continue using Mattermost Professional and Enterprise features.",
|
||||
"announcement_bar.error.trial_license_expiring_last_day.short": "This is the last day of your free trial.",
|
||||
"announcement_bar.non_production_license.message": "Non-production license. Test or staging use only.",
|
||||
"announcement_bar.notification.email_verified": "Email verified",
|
||||
"announcement_bar.warn.contact_support_email": "<a>Contact support</a>.",
|
||||
"announcement_bar.warn.contact_support_text": "To renew your license, contact support at [email protected].",
|
||||
|
||||
Reference in New Issue
Block a user