mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-03 01:18:43 +02:00
feat/telemetry opt out (#4035)
<!-- Thanks for creating a PR! To make it easier for reviewers and everyone else to understand what your changes relate to, please add some relevant content to the headings below. Feel free to ignore or delete sections that you don't think are relevant. Thank you! ❤️ --> ## About the changes <!-- Describe the changes introduced. What are they and why are they being introduced? Feel free to also add screenshots or steps to view the changes if they're visual. --> Adds a UI that shows current status of version and feature usage collection configuration, and a presence in the configuration menu + menu bar. Configuring these features is done by setting environment variables. The version info collection is an existing feature that we're making more visible, the feature usage collection feature is a new feature that has it's own environment configuration but also depends on version info collection being active to work. When version collection is turned off and the experimental feature flag for feature usage collection is turned off: <img width="1269" alt="image" src="https://github.com/Unleash/unleash/assets/707867/435a07da-d238-4b5b-a150-07e3bd6b816f"> When version collection is turned on and the experimental feature flag is off: <img width="1249" alt="image" src="https://github.com/Unleash/unleash/assets/707867/8d1a76c5-99c9-4551-9a4f-86d477bbbf6f"> When the experimental feature flag is enabled, and version+telemetry is turned off: <img width="1239" alt="image" src="https://github.com/Unleash/unleash/assets/707867/e0bc532b-be94-4076-bee1-faef9bc48a5b"> When version collection is turned on, the experimental feature flag is enabled, and telemetry collection is turned off: <img width="1234" alt="image" src="https://github.com/Unleash/unleash/assets/707867/1bd190c1-08fe-4402-bde3-56f863a33289"> When version collection is turned on, the experimental feature flag is enabled, and telemetry collection is turned on: <img width="1229" alt="image" src="https://github.com/Unleash/unleash/assets/707867/848912cd-30bd-43cf-9b81-c58a4cbad1e4"> When version collection is turned off, the experimental feature flag is enabled, and telemetry collection is turned on: <img width="1241" alt="image" src="https://github.com/Unleash/unleash/assets/707867/d2b981f2-033f-4fae-a115-f93e0653729b"> --------- Co-authored-by: sighphyre <liquidwicked64@gmail.com> Co-authored-by: Nuno Góis <github@nunogois.com> Co-authored-by: Thomas Heartman <thomas@getunleash.ai>
This commit is contained in:
parent
73b4ae18c1
commit
3a14b97fdd
@ -10,6 +10,7 @@ import { EditGroupContainer } from './groups/EditGroup/EditGroup';
|
||||
import { Group } from './groups/Group/Group';
|
||||
import { GroupsAdmin } from './groups/GroupsAdmin';
|
||||
import { InstanceAdmin } from './instance-admin/InstanceAdmin';
|
||||
import { InstancePrivacy } from './instance-privacy/InstancePrivacy';
|
||||
import { MaintenanceAdmin } from './maintenance';
|
||||
import AdminMenu from './menu/AdminMenu';
|
||||
import { Network } from './network/Network';
|
||||
@ -46,6 +47,7 @@ export const Admin = () => (
|
||||
<Route path="auth" element={<AuthSettings />} />
|
||||
<Route path="admin-invoices" element={<FlaggedBillingRedirect />} />
|
||||
<Route path="billing" element={<Billing />} />
|
||||
<Route path="instance-privacy" element={<InstancePrivacy />} />
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
|
@ -0,0 +1,147 @@
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import { Box, styled } from '@mui/material';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { InstancePrivacySection } from './InstancePrivacySection';
|
||||
import { useTelemetry } from 'hooks/api/getters/useTelemetry/useTelemetry';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IFeatureActivenessManagementInfo {
|
||||
enabled: IActivenessManagementInfo;
|
||||
disabled: IActivenessManagementInfo;
|
||||
}
|
||||
|
||||
interface IActivenessManagementInfo {
|
||||
environmentVariables: string;
|
||||
changeInfoText: string;
|
||||
}
|
||||
|
||||
const StyledBox = styled(Box)(({ theme }) => ({
|
||||
display: 'grid',
|
||||
gap: theme.spacing(4),
|
||||
}));
|
||||
|
||||
const versionCollectionDetails = {
|
||||
title: 'Version data collection',
|
||||
infoText:
|
||||
"We collect the version of Unleash that you're using. We use this information to inform your Unleash instance of latest updates and critical security patches.",
|
||||
|
||||
concreteDetails: {
|
||||
'Instance ID': 'A unique ID generated for your instance',
|
||||
Version: "The version of Unleash that you're using",
|
||||
},
|
||||
};
|
||||
|
||||
const featureCollectionDetails = {
|
||||
title: 'Feature data collection',
|
||||
infoText:
|
||||
'We collect data about your instance to improve the Unleash product user experience. We may also use the data in case you need help from our support team. Data collection is for internal use only and is not shared with third parties outside Unleash. As we want you to be in control of your data, we will leave it up to you to allow us to collect your data.',
|
||||
concreteDetails: {
|
||||
'Feature toggles': 'The number of feature toggles in your instance',
|
||||
Users: 'The number of users registered in your instance',
|
||||
Projects: 'The number of projects in your instance',
|
||||
'Context Fields': 'The number of custom context fields in use',
|
||||
Groups: 'The number of groups present in your instance',
|
||||
Roles: 'The number of custom roles defined in your instance',
|
||||
Environments: 'The number of environments in your instance',
|
||||
Segments: 'The number of segments defined in your instance',
|
||||
Strategies: 'The number of strategies defined in your instance',
|
||||
'Feature Exports': 'The number of feature exports performed',
|
||||
'Feature Imports': 'The number of feature imports performed',
|
||||
'Custom Strategies':
|
||||
'The number of custom strategies defined in your instance',
|
||||
'Custom Strategies In Use':
|
||||
'The number of custom strategies that are in use by feature toggles',
|
||||
},
|
||||
};
|
||||
|
||||
const versionCollectionActivenessManagementTexts: IFeatureActivenessManagementInfo =
|
||||
{
|
||||
enabled: {
|
||||
environmentVariables: 'CHECK_VERSION=false',
|
||||
changeInfoText:
|
||||
'Version info collection can be disabled by setting the environment variable `CHECK_VERSION` to `false` and restarting Unleash.',
|
||||
},
|
||||
disabled: {
|
||||
environmentVariables: 'CHECK_VERSION=true',
|
||||
changeInfoText:
|
||||
'Version info collection can be enabled by setting the environment variable to true and restarting Unleash.',
|
||||
},
|
||||
};
|
||||
|
||||
const featureCollectionActivenessManagementTexts: IFeatureActivenessManagementInfo =
|
||||
{
|
||||
enabled: {
|
||||
environmentVariables: 'SEND_TELEMETRY=false',
|
||||
changeInfoText:
|
||||
'Feature usage collection can be disabled by setting the environment variable to false and restarting Unleash.',
|
||||
},
|
||||
disabled: {
|
||||
environmentVariables: 'SEND_TELEMETRY=true',
|
||||
changeInfoText:
|
||||
'To enable feature usage collection set the environment variable to true and restart Unleash.',
|
||||
},
|
||||
};
|
||||
|
||||
export const InstancePrivacy = () => {
|
||||
const { settings } = useTelemetry();
|
||||
const { uiConfig, loading } = useUiConfig();
|
||||
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const versionActivenessInfo = settings?.versionInfoCollectionEnabled
|
||||
? versionCollectionActivenessManagementTexts.enabled
|
||||
: versionCollectionActivenessManagementTexts.disabled;
|
||||
|
||||
const featureActivenessInfo = settings?.featureInfoCollectionEnabled
|
||||
? featureCollectionActivenessManagementTexts.enabled
|
||||
: featureCollectionActivenessManagementTexts.disabled;
|
||||
|
||||
let dependsOnFeatureCollection: undefined | string = undefined;
|
||||
if (!settings?.versionInfoCollectionEnabled)
|
||||
dependsOnFeatureCollection = settings?.featureInfoCollectionEnabled
|
||||
? 'Note: Feature usage collection is enabled, but for it to be active you must also enable version info collection'
|
||||
: 'When you enable feature usage collection you must also enable version info collection';
|
||||
|
||||
return (
|
||||
<PageContent header={<PageHeader title="Instance Privacy" />}>
|
||||
<StyledBox>
|
||||
<InstancePrivacySection
|
||||
title={versionCollectionDetails.title}
|
||||
infoText={versionCollectionDetails.infoText}
|
||||
concreteDetails={versionCollectionDetails.concreteDetails}
|
||||
enabled={settings?.versionInfoCollectionEnabled}
|
||||
changeInfoText={versionActivenessInfo.changeInfoText}
|
||||
variablesText={versionActivenessInfo.environmentVariables}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(
|
||||
uiConfig.flags.experimentalExtendedTelemetry
|
||||
)}
|
||||
show={
|
||||
<InstancePrivacySection
|
||||
title={featureCollectionDetails.title}
|
||||
infoText={featureCollectionDetails.infoText}
|
||||
concreteDetails={
|
||||
featureCollectionDetails.concreteDetails
|
||||
}
|
||||
enabled={
|
||||
settings?.featureInfoCollectionEnabled &&
|
||||
settings?.versionInfoCollectionEnabled
|
||||
}
|
||||
changeInfoText={
|
||||
featureActivenessInfo.changeInfoText
|
||||
}
|
||||
variablesText={
|
||||
featureActivenessInfo.environmentVariables
|
||||
}
|
||||
dependsOnText={dependsOnFeatureCollection}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</StyledBox>
|
||||
</PageContent>
|
||||
);
|
||||
};
|
@ -0,0 +1,208 @@
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { Badge } from 'component/common/Badge/Badge';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: theme.spacing(3),
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
}));
|
||||
|
||||
const StyledCardTitleRow = styled(Box)(() => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}));
|
||||
|
||||
const StyledCardDescription = styled(Box)(({ theme }) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: theme.fontSizes.smallBody,
|
||||
marginTop: theme.spacing(2),
|
||||
}));
|
||||
|
||||
const StyledPropertyName = styled('p')(({ theme }) => ({
|
||||
display: 'table-cell',
|
||||
fontWeight: theme.fontWeight.bold,
|
||||
paddingTop: theme.spacing(2),
|
||||
}));
|
||||
|
||||
const StyledPropertyDetails = styled('p')(({ theme }) => ({
|
||||
display: 'table-cell',
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingLeft: theme.spacing(4),
|
||||
}));
|
||||
|
||||
const StyledDataCollectionExplanation = styled('div')(({ theme }) => ({
|
||||
display: 'table-cell',
|
||||
width: '75%',
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingBottom: theme.spacing(2),
|
||||
}));
|
||||
|
||||
const StyledDataCollectionBadge = styled('div')(({ theme }) => ({
|
||||
display: 'table-cell',
|
||||
}));
|
||||
|
||||
const StyledTag = styled('span')(({ theme }) => ({
|
||||
display: 'block',
|
||||
textAlign: 'right',
|
||||
color: theme.palette.neutral.dark,
|
||||
}));
|
||||
|
||||
const StyledDescription = styled('div')(({ theme }) => ({
|
||||
maxWidth: theme.spacing(50),
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: theme.fontSizes.smallBody,
|
||||
}));
|
||||
|
||||
const StyledDataCollectionPropertyRow = styled('div')(() => ({
|
||||
display: 'table-row',
|
||||
}));
|
||||
|
||||
const StyledDataCollectionPropertyTable = styled('div')(() => ({
|
||||
display: 'table',
|
||||
}));
|
||||
|
||||
const StyledDataCollectionPropertyCell = styled('div')(() => ({
|
||||
display: 'table-cell',
|
||||
}));
|
||||
|
||||
interface IToolTipInstructionContentProps {
|
||||
changeInfoText: string;
|
||||
variablesText: string;
|
||||
dependsOnText?: string;
|
||||
}
|
||||
|
||||
const ToolTipInstructionContent = ({
|
||||
changeInfoText,
|
||||
variablesText,
|
||||
dependsOnText,
|
||||
}: IToolTipInstructionContentProps) => {
|
||||
return (
|
||||
<StyledDescription>
|
||||
<ToolTipDescriptionText>{changeInfoText}</ToolTipDescriptionText>
|
||||
|
||||
<ToolTipDescriptionCode>
|
||||
<div>{variablesText}</div>
|
||||
</ToolTipDescriptionCode>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={Boolean(dependsOnText)}
|
||||
show={
|
||||
<ToolTipDescriptionText>
|
||||
{dependsOnText}
|
||||
</ToolTipDescriptionText>
|
||||
}
|
||||
/>
|
||||
</StyledDescription>
|
||||
);
|
||||
};
|
||||
|
||||
const ToolTipDescriptionCode = styled('code')(({ theme }) => ({
|
||||
display: 'block',
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.application,
|
||||
fontSize: theme.fontSizes.smallerBody,
|
||||
marginTop: theme.spacing(1),
|
||||
padding: theme.spacing(1),
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderWidth: 1,
|
||||
wordWrap: 'break-word',
|
||||
whiteSpace: 'pre-wrap',
|
||||
fontFamily: 'monospace',
|
||||
lineHeight: 1.5,
|
||||
}));
|
||||
|
||||
const ToolTipDescriptionText = styled('p')(({ theme }) => ({
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: theme.fontSizes.smallBody,
|
||||
marginTop: theme.spacing(1),
|
||||
}));
|
||||
|
||||
interface IInstancePrivacySectionProps {
|
||||
title: string;
|
||||
infoText: string;
|
||||
concreteDetails: Record<string, string>;
|
||||
enabled: boolean;
|
||||
changeInfoText: string;
|
||||
variablesText: string;
|
||||
dependsOnText?: string;
|
||||
}
|
||||
|
||||
export const InstancePrivacySection = ({
|
||||
title,
|
||||
infoText,
|
||||
concreteDetails,
|
||||
enabled,
|
||||
changeInfoText,
|
||||
variablesText,
|
||||
dependsOnText,
|
||||
}: IInstancePrivacySectionProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledCardTitleRow>
|
||||
<b>{title}</b>
|
||||
<StyledDataCollectionBadge>
|
||||
<ConditionallyRender
|
||||
condition={enabled}
|
||||
show={
|
||||
<Badge color="success" icon={<CheckIcon />}>
|
||||
Data is collected
|
||||
</Badge>
|
||||
}
|
||||
elseShow={
|
||||
<Badge color="neutral" icon={<ClearIcon />}>
|
||||
No data is collected
|
||||
</Badge>
|
||||
}
|
||||
/>
|
||||
</StyledDataCollectionBadge>
|
||||
</StyledCardTitleRow>
|
||||
|
||||
<StyledCardDescription>
|
||||
<StyledDataCollectionPropertyTable>
|
||||
<StyledDataCollectionExplanation>
|
||||
{infoText}
|
||||
</StyledDataCollectionExplanation>
|
||||
<StyledDataCollectionPropertyCell>
|
||||
<StyledTag>
|
||||
<TooltipLink
|
||||
tooltip={
|
||||
<ToolTipInstructionContent
|
||||
changeInfoText={changeInfoText}
|
||||
variablesText={variablesText}
|
||||
dependsOnText={dependsOnText}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{enabled
|
||||
? 'How to disable collecting data?'
|
||||
: 'How to enable collecting data?'}
|
||||
</TooltipLink>
|
||||
</StyledTag>
|
||||
</StyledDataCollectionPropertyCell>
|
||||
</StyledDataCollectionPropertyTable>
|
||||
|
||||
<StyledDataCollectionPropertyTable>
|
||||
{Object.entries(concreteDetails).map(([key, value]) => {
|
||||
return (
|
||||
<StyledDataCollectionPropertyRow key={key}>
|
||||
<StyledPropertyName>{key}</StyledPropertyName>
|
||||
<StyledPropertyDetails>
|
||||
{value}
|
||||
</StyledPropertyDetails>
|
||||
</StyledDataCollectionPropertyRow>
|
||||
);
|
||||
})}
|
||||
</StyledDataCollectionPropertyTable>
|
||||
</StyledCardDescription>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
@ -19,6 +19,7 @@ const StyledBox = styled(Box)(({ theme }) => ({
|
||||
display: 'grid',
|
||||
gap: theme.spacing(4),
|
||||
}));
|
||||
|
||||
const MaintenancePage = () => {
|
||||
const { loading } = useUiConfig();
|
||||
|
||||
|
@ -119,6 +119,15 @@ function AdminMenu() {
|
||||
}
|
||||
/>
|
||||
|
||||
<Tab
|
||||
value="instance-privacy"
|
||||
label={
|
||||
<CenteredNavLink to="/admin/instance-privacy">
|
||||
Instance privacy
|
||||
</CenteredNavLink>
|
||||
}
|
||||
/>
|
||||
|
||||
{isBilling && (
|
||||
<Tab
|
||||
value="billing"
|
||||
|
@ -505,6 +505,11 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
|
||||
title: 'Billing & invoices',
|
||||
menu: { adminSettings: true, mode: ['pro'] },
|
||||
},
|
||||
{
|
||||
path: '/admin/instance-privacy',
|
||||
title: 'Instance privacy',
|
||||
menu: { adminSettings: true },
|
||||
},
|
||||
];
|
||||
|
||||
export const getRoute = (path: string) =>
|
||||
|
39
frontend/src/hooks/api/getters/useTelemetry/useTelemetry.ts
Normal file
39
frontend/src/hooks/api/getters/useTelemetry/useTelemetry.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import useSWR from 'swr';
|
||||
import { useMemo } from 'react';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
|
||||
export interface ITelemetrySettings {
|
||||
versionInfoCollectionEnabled: boolean;
|
||||
featureInfoCollectionEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface ITelemetrySettingsResponse {
|
||||
settings: ITelemetrySettings;
|
||||
refetchGroup: () => void;
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export const useTelemetry = (): ITelemetrySettingsResponse => {
|
||||
const { data, error, mutate } = useSWR(
|
||||
formatApiPath(`api/admin/telemetry/settings`),
|
||||
fetcher
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
settings: data,
|
||||
loading: !error && !data,
|
||||
refetchGroup: () => mutate(),
|
||||
error,
|
||||
}),
|
||||
[data, error, mutate]
|
||||
);
|
||||
};
|
||||
|
||||
const fetcher = (path: string) => {
|
||||
return fetch(path)
|
||||
.then(handleErrorResponses('Telemetry Settings'))
|
||||
.then(res => res.json());
|
||||
};
|
@ -54,6 +54,7 @@ export interface IFlags {
|
||||
advancedPlayground?: boolean;
|
||||
customRootRoles?: boolean;
|
||||
strategySplittedButton?: boolean;
|
||||
experimentalExtendedTelemetry?: boolean;
|
||||
}
|
||||
|
||||
export interface IVersionInfo {
|
||||
|
@ -141,6 +141,7 @@ import {
|
||||
variantsSchema,
|
||||
versionSchema,
|
||||
advancedPlaygroundFeatureSchema,
|
||||
telemetrySettingsSchema,
|
||||
} from './spec';
|
||||
import { IServerOption } from '../types';
|
||||
import { mapValues, omitKeys } from '../util';
|
||||
@ -339,6 +340,7 @@ export const schemas: UnleashSchemas = {
|
||||
importTogglesValidateSchema,
|
||||
importTogglesValidateItemSchema,
|
||||
contextFieldStrategiesSchema,
|
||||
telemetrySettingsSchema,
|
||||
};
|
||||
|
||||
// Remove JSONSchema keys that would result in an invalid OpenAPI spec.
|
||||
|
@ -140,3 +140,4 @@ export * from './admin-count-schema';
|
||||
export * from './advanced-playground-feature-schema';
|
||||
export * from './advanced-playground-response-schema';
|
||||
export * from './advanced-playground-request-schema';
|
||||
export * from './telemetry-settings-schema';
|
||||
|
29
src/lib/openapi/spec/telemetry-settings-schema.ts
Normal file
29
src/lib/openapi/spec/telemetry-settings-schema.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { FromSchema } from 'json-schema-to-ts';
|
||||
|
||||
export const telemetrySettingsSchema = {
|
||||
$id: '#/components/schemas/telemetrySettingsSchema',
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['versionInfoCollectionEnabled', 'featureInfoCollectionEnabled'],
|
||||
description:
|
||||
'Contains information about which settings are configured for version info collection and feature usage collection.',
|
||||
properties: {
|
||||
versionInfoCollectionEnabled: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether collection of version info is enabled/active.',
|
||||
example: true,
|
||||
},
|
||||
featureInfoCollectionEnabled: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether collection of feature usage metrics is enabled/active.',
|
||||
example: true,
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
} as const;
|
||||
|
||||
export type TelemetrySettingsSchema = FromSchema<
|
||||
typeof telemetrySettingsSchema
|
||||
>;
|
@ -124,6 +124,10 @@ const OPENAPI_TAGS = [
|
||||
description:
|
||||
'API for managing [change requests](https://docs.getunleash.io/reference/change-requests).',
|
||||
},
|
||||
{
|
||||
name: 'Telemetry',
|
||||
description: 'API for information about telemetry collection',
|
||||
},
|
||||
] as const;
|
||||
|
||||
// make the export mutable, so it can be used in a schema
|
||||
|
@ -26,6 +26,7 @@ import ConstraintsController from './constraints';
|
||||
import PatController from './user/pat';
|
||||
import { PublicSignupController } from './public-signup';
|
||||
import InstanceAdminController from './instance-admin';
|
||||
import TelemetryController from './telemetry';
|
||||
import FavoritesController from './favorites';
|
||||
import MaintenanceController from './maintenance';
|
||||
import { createKnexTransactionStarter } from '../../db/transaction';
|
||||
@ -137,6 +138,11 @@ class AdminApi extends Controller {
|
||||
'/maintenance',
|
||||
new MaintenanceController(config, services).router,
|
||||
);
|
||||
|
||||
this.app.use(
|
||||
'/telemetry',
|
||||
new TelemetryController(config, services).router,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
66
src/lib/routes/admin-api/telemetry.ts
Normal file
66
src/lib/routes/admin-api/telemetry.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Response } from 'express';
|
||||
import { OpenApiService } from 'lib/services';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
import { IUnleashConfig } from '../../types/option';
|
||||
import Controller from '../controller';
|
||||
import { NONE } from '../../types/permissions';
|
||||
import { IUnleashServices } from 'lib/types';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
telemetrySettingsSchema,
|
||||
TelemetrySettingsSchema,
|
||||
} from '../../openapi/spec/telemetry-settings-schema';
|
||||
|
||||
class TelemetryController extends Controller {
|
||||
config: IUnleashConfig;
|
||||
|
||||
openApiService: OpenApiService;
|
||||
|
||||
constructor(
|
||||
config: IUnleashConfig,
|
||||
{ openApiService }: Pick<IUnleashServices, 'openApiService'>,
|
||||
) {
|
||||
super(config);
|
||||
this.config = config;
|
||||
this.openApiService = openApiService;
|
||||
|
||||
this.route({
|
||||
method: 'get',
|
||||
path: '/settings',
|
||||
handler: this.getTelemetrySettings,
|
||||
permission: NONE,
|
||||
middleware: [
|
||||
openApiService.validPath({
|
||||
tags: ['Telemetry'],
|
||||
summary: 'Get telemetry settings',
|
||||
description:
|
||||
'Provides the configured settings for [telemetry information collection](https://docs.getunleash.io/topics/data-collection)',
|
||||
operationId: 'getTelemetrySettings',
|
||||
responses: {
|
||||
200: createResponseSchema('telemetrySettingsSchema'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async getTelemetrySettings(
|
||||
req: IAuthRequest,
|
||||
res: Response<TelemetrySettingsSchema>,
|
||||
): Promise<void> {
|
||||
this.openApiService.respondWithValidation(
|
||||
200,
|
||||
res,
|
||||
telemetrySettingsSchema.$id,
|
||||
{
|
||||
versionInfoCollectionEnabled: this.config.versionCheck.enable,
|
||||
featureInfoCollectionEnabled:
|
||||
this.config.flagResolver.isEnabled(
|
||||
'experimentalExtendedTelemetry',
|
||||
) && this.config.telemetry,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TelemetryController;
|
@ -5322,6 +5322,27 @@ Stats are divided into current and previous **windows**.
|
||||
],
|
||||
"type": "object",
|
||||
},
|
||||
"telemetrySettingsSchema": {
|
||||
"additionalProperties": false,
|
||||
"description": "Contains information about which settings are configured for version info collection and feature usage collection.",
|
||||
"properties": {
|
||||
"featureInfoCollectionEnabled": {
|
||||
"description": "Whether collection of feature usage metrics is enabled/active.",
|
||||
"example": true,
|
||||
"type": "boolean",
|
||||
},
|
||||
"versionInfoCollectionEnabled": {
|
||||
"description": "Whether collection of version info is enabled/active.",
|
||||
"example": true,
|
||||
"type": "boolean",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"versionInfoCollectionEnabled",
|
||||
"featureInfoCollectionEnabled",
|
||||
],
|
||||
"type": "object",
|
||||
},
|
||||
"toggleMaintenanceSchema": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
@ -13088,6 +13109,28 @@ true,false,"[{""range"":""allTime"",""count"":15},{""range"":""30d"",""count"":9
|
||||
],
|
||||
},
|
||||
},
|
||||
"/api/admin/telemetry/settings": {
|
||||
"get": {
|
||||
"description": "Provides the configured settings for [telemetry information collection](https://docs.getunleash.io/topics/data-collection)",
|
||||
"operationId": "getTelemetrySettings",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/telemetrySettingsSchema",
|
||||
},
|
||||
},
|
||||
},
|
||||
"description": "telemetrySettingsSchema",
|
||||
},
|
||||
},
|
||||
"summary": "Get telemetry settings",
|
||||
"tags": [
|
||||
"Telemetry",
|
||||
],
|
||||
},
|
||||
},
|
||||
"/api/admin/ui-config": {
|
||||
"get": {
|
||||
"operationId": "getUiConfig",
|
||||
@ -14587,6 +14630,10 @@ true,false,"[{""range"":""allTime"",""count"":15},{""range"":""30d"",""count"":9
|
||||
"description": "Create, update, and delete [tags and tag types](https://docs.getunleash.io/reference/tags).",
|
||||
"name": "Tags",
|
||||
},
|
||||
{
|
||||
"description": "API for information about telemetry collection",
|
||||
"name": "Telemetry",
|
||||
},
|
||||
{
|
||||
"description": "Experimental endpoints that may change or disappear at any time.",
|
||||
"name": "Unstable",
|
||||
|
Loading…
Reference in New Issue
Block a user