* fix fileutils.TestFindFile on MacOS

* introduce model.ExternalServiceEnvironment

* pick license public key from external service env

* pick Stripe public key from external service env

* pick Rudder key from external service env

* configure Sentry DSN from external service env

* always log external_service_environment, Unsetenv

* clear faked BuildEnv, improve logging

* strip out unset GOTAGS

* fix Sentry tests

* simplify to just ServiceEnvironment

* relocate ServiceEnvironment in client config

* initialize CWS URLs based on service environment

* unset rudder key for boards dev

* harden service environment to avoid accidental production

* fix TestSentry again

* fix DEFAULT -> ENTERPRISE

* s/dev/test when naming playbooks rudder key

* simplify boards rudder key switch

* use uniform rudderKey variable names

* retain compatibility with existing pipeline

* reduce to just production/test

* unit test with valid test license

* simplify Playbooks telemetry initialization

* restore dev service environment

* emit ServiceEnvironment when running e2e tests
Этот коммит содержится в:
Jesse Hallam
2023-06-07 10:15:33 -03:00
коммит произвёл GitHub
родитель 6c82605df0
Коммит 305fac6507
37 изменённых файлов: 552 добавлений и 304 удалений

Просмотреть файл

@@ -12,6 +12,8 @@ import {SuiteWindow} from 'src/types/index'
import {PluginRegistry} from 'src/types/mattermost-webapp'
import {ServiceEnvironment} from '@mattermost/types/config'
import {RudderTelemetryHandler, rudderAnalytics} from 'src/rudder'
import appBarIcon from 'static/app-bar-icon.png'
@@ -83,8 +85,19 @@ function getSubpath(siteURL: string): string {
return url.pathname.replace(/\/+$/, '')
}
const TELEMETRY_RUDDER_KEY = 'placeholder_boards_rudder_key'
const TELEMETRY_RUDDER_DATAPLANE_URL = 'placeholder_rudder_dataplane_url'
const TELEMETRY_RUDDER_URL = 'https://pdat.matterlytics.com'
const TELEMETRY_RUDDER_KEY_PROD = '1myWcDbTkIThnpPYyms7DKlmQWl'
const TELEMETRY_RUDDER_KEY_TEST = '1myWYwHRDFdLDTpznQ7qFlOPQaa'
// TO_BE_DEPRECATED_* are placeholders to allow the existing release pipelines to run without
// failing to insert the values that are now hard-coded above. Remove this once we converge
// on the unified delivery pipeline in GitHub.
//
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const TO_BE_DEPRECATED_TELEMETRY_RUDDER_URL = 'placeholder_rudder_dataplane_url'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const TO_BE_DEPRECATAED_TELEMETRY_RUDDER_KEY = 'placeholder_boards_rudder_key'
const TELEMETRY_OPTIONS = {
context: {
ip: '0.0.0.0',
@@ -512,12 +525,17 @@ export default class Plugin {
const config = await octoClient.getClientConfig()
if (config?.telemetry) {
let rudderKey = TELEMETRY_RUDDER_KEY
let rudderUrl = TELEMETRY_RUDDER_DATAPLANE_URL
if (rudderKey.startsWith('placeholder') && rudderUrl.startsWith('placeholder')) {
rudderKey = process.env.RUDDER_KEY as string //eslint-disable-line no-process-env
rudderUrl = process.env.RUDDER_DATAPLANE_URL as string //eslint-disable-line no-process-env
const rudderUrl = TELEMETRY_RUDDER_URL
let rudderKey = ''
switch (mmStore.getState().entities.general.config.ServiceEnvironment) {
case ServiceEnvironment.PRODUCTION:
rudderKey = TELEMETRY_RUDDER_KEY_PROD
break
case ServiceEnvironment.TEST:
rudderKey = TELEMETRY_RUDDER_KEY_TEST
break
case ServiceEnvironment.DEV:
break
}
if (rudderKey !== '') {

Просмотреть файл

@@ -172,15 +172,6 @@ config.output = {
chunkFilename: '[name].[contenthash].js',
};
/* eslint-disable no-process-env */
const env = {};
env.RUDDER_KEY = JSON.stringify(process.env.RUDDER_KEY || '');
env.RUDDER_DATAPLANE_URL = JSON.stringify(process.env.RUDDER_DATAPLANE_URL || '');
config.plugins.push(new webpack.DefinePlugin({
'process.env': env,
}));
config.plugins.push(new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[name].[contenthash].css',

Просмотреть файл

@@ -25,7 +25,7 @@ import {CloudLinks} from 'utils/constants';
import BlockableLink from 'components/admin_console/blockable_link';
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
import PaymentForm from 'components/payment_form/payment_form';
import {STRIPE_CSS_SRC, STRIPE_PUBLIC_KEY} from 'components/payment_form/stripe';
import {STRIPE_CSS_SRC, getStripePublicKey} from 'components/payment_form/stripe';
import SaveButton from 'components/save_button';
import AlertBanner from 'components/alert_banner';
import AdminHeader from 'components/widgets/admin_console/admin_header';
@@ -58,6 +58,8 @@ const PaymentInfoEdit: React.FC = () => {
card: {} as any,
});
const stripePublicKey = useSelector((state: GlobalState) => getStripePublicKey(state));
useEffect(() => {
dispatch(getCloudCustomer());
}, []);
@@ -83,7 +85,7 @@ const PaymentInfoEdit: React.FC = () => {
};
if (!stripePromise) {
stripePromise = loadStripe(STRIPE_PUBLIC_KEY);
stripePromise = loadStripe(stripePublicKey);
}
return (

Просмотреть файл

@@ -2,22 +2,26 @@
// See LICENSE.txt for license information.
import {useEffect, useRef, useState} from 'react';
import {useSelector} from 'react-redux';
import {Stripe} from '@stripe/stripe-js';
import {loadStripe} from '@stripe/stripe-js/pure'; // https://github.com/stripe/stripe-js#importing-loadstripe-without-side-effects
import {STRIPE_PUBLIC_KEY} from 'components/payment_form/stripe';
import {GlobalState} from 'types/store';
import {getStripePublicKey} from 'components/payment_form/stripe';
// reloadHint
export default function useLoadStripe(reloadHint?: number) {
const stripeRef = useRef<Stripe | null>(null);
const [, setDone] = useState(false);
const stripePublicKey = useSelector((state: GlobalState) => getStripePublicKey(state));
useEffect(() => {
if (stripeRef.current) {
return;
}
loadStripe(STRIPE_PUBLIC_KEY).then((stripe: Stripe | null) => {
loadStripe(stripePublicKey).then((stripe: Stripe | null) => {
stripeRef.current = stripe;
// deliberately cause a rerender so that the input can render.

Просмотреть файл

@@ -10,6 +10,9 @@ import {
SetupIntent,
} from '@stripe/stripe-js';
import {GlobalState} from 'types/store';
import {ServiceEnvironment} from '@mattermost/types/config';
type ConfirmCardSetupType = (clientSecret: string, data?: ConfirmCardSetupData | undefined, options?: ConfirmCardSetupOptions | undefined) => Promise<{ setupIntent?: SetupIntent | undefined; error?: StripeError | undefined }> | undefined;
function prodConfirmCardSetup(confirmCardSetup: ConfirmCardSetupType): ConfirmCardSetupType {
@@ -26,4 +29,15 @@ export const getConfirmCardSetup = (isCwsMockMode?: boolean) => (isCwsMockMode ?
export const STRIPE_CSS_SRC = 'https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,600i&display=swap';
//eslint-disable-next-line no-process-env
export const STRIPE_PUBLIC_KEY = process.env.STRIPE_PUBLIC_KEY || 'pk_test_ttEpW6dCHksKyfAFzh6MvgBj';
export const getStripePublicKey = (state: GlobalState) => {
switch (state.entities.general.config.ServiceEnvironment) {
case ServiceEnvironment.PRODUCTION:
return 'pk_live_cDF5gYLPf5vQjJ7jp71p7GRK';
case ServiceEnvironment.TEST:
case ServiceEnvironment.DEV:
return 'pk_test_ttEpW6dCHksKyfAFzh6MvgBj';
}
return '';
};

Просмотреть файл

@@ -30,6 +30,8 @@ import withGetCloudSubscription from 'components/common/hocs/cloud/with_get_clou
import {findOnlyYearlyProducts} from 'utils/products';
import {getCloudContactSalesLink, getCloudSupportLink} from 'utils/contact_support_sales';
import {getStripePublicKey} from 'components/payment_form/stripe';
const PurchaseModal = makeAsyncComponent('PurchaseModal', React.lazy(() => import('./purchase_modal')));
function mapStateToProps(state: GlobalState) {
@@ -46,6 +48,7 @@ function mapStateToProps(state: GlobalState) {
const companyName = customer?.name || '';
const contactSalesLink = getCloudContactSalesLink(firstName, lastName, companyName, customerEmail, 'mattermost', 'in-product-cloud');
const contactSupportLink = getCloudSupportLink(customerEmail, 'Cloud purchase', '', window.location.host);
const stripePublicKey = getStripePublicKey(state);
return {
show: isModalOpen(state, ModalIdentifiers.CLOUD_PURCHASE),
@@ -64,6 +67,7 @@ function mapStateToProps(state: GlobalState) {
theme: getTheme(state),
isDelinquencyModal,
usersCount: Number(getAdminAnalytics(state)!.TOTAL_USERS) || 1,
stripePublicKey,
};
}
type Actions = {

Просмотреть файл

@@ -36,7 +36,7 @@ import {
import {goToMattermostContactSalesForm} from 'utils/contact_support_sales';
import PaymentDetails from 'components/admin_console/billing/payment_details';
import {STRIPE_CSS_SRC, STRIPE_PUBLIC_KEY} from 'components/payment_form/stripe';
import {STRIPE_CSS_SRC} from 'components/payment_form/stripe';
import RootPortal from 'components/root_portal';
import FullScreenModal from 'components/widgets/modals/full_screen_modal';
import OverlayTrigger from 'components/overlay_trigger';
@@ -130,6 +130,8 @@ type Props = {
// callerCTA is information about the cta that opened this modal. This helps us provide a telemetry path
// showing information about how the modal was opened all the way to more CTAs within the modal itself
callerCTA?: string;
stripePublicKey: string;
actions: {
openModal: <P>(modalData: ModalData<P>) => void;
closeModal: () => void;
@@ -964,7 +966,7 @@ class PurchaseModal extends React.PureComponent<Props, State> {
);
}
if (!stripePromise) {
stripePromise = loadStripe(STRIPE_PUBLIC_KEY);
stripePromise = loadStripe(this.props.stripePublicKey);
}
return (

Просмотреть файл

@@ -16,6 +16,9 @@ import * as GlobalActions from 'actions/global_actions';
import Constants, {StoragePrefixes, WindowSizes} from 'utils/constants';
import matchMedia from 'tests/helpers/match_media.mock';
import {ProductComponent} from 'types/store/plugins';
import {ServiceEnvironment} from '@mattermost/types/config';
import store from 'stores/redux_store.jsx';
jest.mock('rudder-sdk-js', () => ({
identify: jest.fn(),
@@ -201,12 +204,16 @@ describe('components/Root', () => {
describe('onConfigLoaded', () => {
afterEach(() => {
Client4.telemetryHandler = undefined;
Constants.TELEMETRY_RUDDER_KEY = 'placeholder_rudder_key';
Constants.TELEMETRY_RUDDER_DATAPLANE_URL = 'placeholder_rudder_dataplane_url';
});
test('should not set a TelemetryHandler when onConfigLoaded is called if Rudder is not configured', () => {
store.dispatch({
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
data: {
ServiceEnvironment: ServiceEnvironment.DEV,
},
});
const wrapper = shallow(<Root {...baseProps}/>);
Client4.trackEvent('category', 'event');
@@ -217,8 +224,12 @@ describe('components/Root', () => {
});
test('should set a TelemetryHandler when onConfigLoaded is called if Rudder is configured', () => {
Constants.TELEMETRY_RUDDER_KEY = 'testKey';
Constants.TELEMETRY_RUDDER_DATAPLANE_URL = 'url';
store.dispatch({
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
data: {
ServiceEnvironment: ServiceEnvironment.TEST,
},
});
const wrapper = shallow(<Root {...baseProps}/>);
@@ -236,8 +247,12 @@ describe('components/Root', () => {
// Simulate an error occurring and the callback not getting called
});
Constants.TELEMETRY_RUDDER_KEY = 'testKey';
Constants.TELEMETRY_RUDDER_DATAPLANE_URL = 'url';
store.dispatch({
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
data: {
ServiceEnvironment: ServiceEnvironment.TEST,
},
});
const wrapper = shallow(<Root {...baseProps}/>);

Просмотреть файл

@@ -93,6 +93,7 @@ import {applyLuxonDefaults} from './effects';
import RootProvider from './root_provider';
import RootRedirect from './root_redirect';
import {ServiceEnvironment} from '@mattermost/types/config';
const CreateTeam = makeAsyncComponent('CreateTeam', LazyCreateTeam);
const ErrorPage = makeAsyncComponent('ErrorPage', LazyErrorPage);
@@ -229,17 +230,23 @@ export default class Root extends React.PureComponent<Props, State> {
}
onConfigLoaded = () => {
const config = getConfig(store.getState());
const telemetryId = this.props.telemetryId;
let rudderKey: string | null | undefined = Constants.TELEMETRY_RUDDER_KEY;
let rudderUrl: string | null | undefined = Constants.TELEMETRY_RUDDER_DATAPLANE_URL;
if (rudderKey.startsWith('placeholder') && rudderUrl.startsWith('placeholder')) {
rudderKey = process.env.RUDDER_KEY; //eslint-disable-line no-process-env
rudderUrl = process.env.RUDDER_DATAPLANE_URL; //eslint-disable-line no-process-env
const rudderUrl = 'https://pdat.matterlytics.com';
let rudderKey = '';
switch (config.ServiceEnvironment) {
case ServiceEnvironment.PRODUCTION:
rudderKey = '1aoejPqhgONMI720CsBSRWzzRQ9';
break;
case ServiceEnvironment.TEST:
rudderKey = '1aoeoCDeh7OCHcbW2kseWlwUFyq';
break;
case ServiceEnvironment.DEV:
break;
}
if (rudderKey != null && rudderKey !== '' && this.props.telemetryEnabled) {
if (rudderKey !== '' && this.props.telemetryEnabled) {
const rudderCfg: {setCookieDomain?: string} = {};
const siteURL = getConfig(store.getState()).SiteURL;
if (siteURL !== '') {

Просмотреть файл

@@ -1974,8 +1974,6 @@ export const Constants = {
AUTOCOMPLETE_SPLIT_CHARACTERS: ['.', '-', '_'],
ANIMATION_TIMEOUT: 1000,
SEARCH_TIMEOUT_MILLISECONDS: 100,
TELEMETRY_RUDDER_KEY: 'placeholder_rudder_key',
TELEMETRY_RUDDER_DATAPLANE_URL: 'placeholder_rudder_dataplane_url',
TEAMMATE_NAME_DISPLAY: {
SHOW_USERNAME: 'username',
SHOW_NICKNAME_FULLNAME: 'nickname_full_name',

Просмотреть файл

@@ -410,20 +410,14 @@ if (DEV) {
config.devtool = 'source-map';
}
const env = {
STRIPE_PUBLIC_KEY: JSON.stringify(process.env.STRIPE_PUBLIC_KEY || ''),
};
const env = {};
if (DEV) {
env.PUBLIC_PATH = JSON.stringify(publicPath);
env.RUDDER_KEY = JSON.stringify(process.env.RUDDER_KEY || '');
env.RUDDER_DATAPLANE_URL = JSON.stringify(process.env.RUDDER_DATAPLANE_URL || '');
if (process.env.MM_LIVE_RELOAD) {
config.plugins.push(new LiveReloadPlugin());
}
} else {
env.NODE_ENV = JSON.stringify('production');
env.RUDDER_KEY = JSON.stringify(process.env.RUDDER_KEY || '');
env.RUDDER_DATAPLANE_URL = JSON.stringify(process.env.RUDDER_DATAPLANE_URL || '');
}
config.plugins.push(new webpack.DefinePlugin({

Просмотреть файл

@@ -203,6 +203,7 @@ export type ClientConfig = {
PersistentNotificationIntervalMinutes: string;
AllowPersistentNotificationsForGuests: string;
DelayChannelAutocomplete: 'true' | 'false';
ServiceEnvironment: string;
};
export type License = {
@@ -948,3 +949,9 @@ export enum CollapsedThreads {
DEFAULT_OFF = 'default_off',
ALWAYS_ON = 'always_on',
}
export enum ServiceEnvironment {
PRODUCTION = 'production',
TEST = 'test',
DEV = 'dev',
}