diff --git a/frontend/src/component/common/ApiTokenTable/ApiTokenTable.tsx b/frontend/src/component/common/ApiTokenTable/ApiTokenTable.tsx index edb852a1e3..9c50cdaa0a 100644 --- a/frontend/src/component/common/ApiTokenTable/ApiTokenTable.tsx +++ b/frontend/src/component/common/ApiTokenTable/ApiTokenTable.tsx @@ -86,7 +86,7 @@ export const ApiTokenTable = ({ {'No tokens available. Read '} diff --git a/frontend/src/component/impact-metrics/ChartConfigModal.tsx b/frontend/src/component/impact-metrics/ChartConfigModal.tsx index 40a480aa77..1e8202e118 100644 --- a/frontend/src/component/impact-metrics/ChartConfigModal.tsx +++ b/frontend/src/component/impact-metrics/ChartConfigModal.tsx @@ -120,6 +120,7 @@ export const ChartConfigModal: FC = ({ selectedRange={formData.selectedRange} selectedLabels={formData.selectedLabels} beginAtZero={formData.beginAtZero} + showRate={formData.showRate} /> diff --git a/frontend/src/component/impact-metrics/ChartItem.tsx b/frontend/src/component/impact-metrics/ChartItem.tsx index 6793670a1e..029637eb40 100644 --- a/frontend/src/component/impact-metrics/ChartItem.tsx +++ b/frontend/src/component/impact-metrics/ChartItem.tsx @@ -21,6 +21,10 @@ const getConfigDescription = (config: ChartConfig): string => { parts.push(`last ${config.selectedRange}`); + if (config.showRate) { + parts.push('rate per second'); + } + const labelCount = Object.keys(config.selectedLabels).length; if (labelCount > 0) { parts.push(`${labelCount} filter${labelCount > 1 ? 's' : ''}`); @@ -29,15 +33,6 @@ const getConfigDescription = (config: ChartConfig): string => { return parts.join(' • '); }; -const StyledChartWrapper = styled(Box)({ - height: '100%', - width: '100%', - '& > div': { - height: '100% !important', - width: '100% !important', - }, -}); - const StyledWidget = styled(Paper)(({ theme }) => ({ borderRadius: `${theme.shape.borderRadiusMedium}px`, boxShadow: 'none', @@ -127,17 +122,16 @@ export const ChartItem: FC = ({ config, onEdit, onDelete }) => ( - - - + diff --git a/frontend/src/component/impact-metrics/ImpactMetrics.tsx b/frontend/src/component/impact-metrics/ImpactMetrics.tsx index 3cbbbc5027..6c6ba9cff6 100644 --- a/frontend/src/component/impact-metrics/ImpactMetrics.tsx +++ b/frontend/src/component/impact-metrics/ImpactMetrics.tsx @@ -9,6 +9,8 @@ import { ChartItem } from './ChartItem.tsx'; import { GridLayoutWrapper, type GridItem } from './GridLayoutWrapper.tsx'; import { useImpactMetricsState } from './hooks/useImpactMetricsState.ts'; import type { ChartConfig, LayoutItem } from './types.ts'; +import useToast from 'hooks/useToast'; +import { formatUnknownError } from 'utils/formatUnknownError'; const StyledEmptyState = styled(Paper)(({ theme }) => ({ textAlign: 'center', @@ -21,9 +23,18 @@ const StyledEmptyState = styled(Paper)(({ theme }) => ({ export const ImpactMetrics: FC = () => { const [modalOpen, setModalOpen] = useState(false); const [editingChart, setEditingChart] = useState(); + const { setToastApiError } = useToast(); - const { charts, layout, addChart, updateChart, deleteChart, updateLayout } = - useImpactMetricsState(); + const { + charts, + layout, + loading: settingsLoading, + error: settingsError, + addChart, + updateChart, + deleteChart, + updateLayout, + } = useImpactMetricsState(); const { metadata, @@ -51,20 +62,39 @@ export const ImpactMetrics: FC = () => { setModalOpen(true); }; - const handleSaveChart = (config: Omit) => { - if (editingChart) { - updateChart(editingChart.id, config); - } else { - addChart(config); + const handleSaveChart = async (config: Omit) => { + try { + if (editingChart) { + await updateChart(editingChart.id, config); + } else { + await addChart(config); + } + setModalOpen(false); + } catch (error) { + setToastApiError(formatUnknownError(error)); } - setModalOpen(false); }; const handleLayoutChange = useCallback( - (layout: any[]) => { - updateLayout(layout as LayoutItem[]); + async (layout: any[]) => { + try { + await updateLayout(layout as LayoutItem[]); + } catch (error) { + setToastApiError(formatUnknownError(error)); + } }, - [updateLayout], + [updateLayout, setToastApiError], + ); + + const handleDeleteChart = useCallback( + async (id: string) => { + try { + await deleteChart(id); + } catch (error) { + setToastApiError(formatUnknownError(error)); + } + }, + [deleteChart], ); const gridItems: GridItem[] = useMemo( @@ -79,7 +109,7 @@ export const ImpactMetrics: FC = () => { ), w: existingLayout?.w ?? 6, @@ -92,10 +122,11 @@ export const ImpactMetrics: FC = () => { maxH: 8, }; }), - [charts, layout, handleEditChart, deleteChart], + [charts, layout, handleEditChart, handleDeleteChart], ); - const hasError = metadataError; + const hasError = metadataError || settingsError; + const isLoading = metadataLoading || settingsLoading; return ( <> @@ -111,14 +142,14 @@ export const ImpactMetrics: FC = () => { variant='contained' startIcon={} onClick={handleAddChart} - disabled={metadataLoading || !!hasError} + disabled={isLoading || !!hasError} > Add Chart } /> - {charts.length === 0 && !metadataLoading && !hasError ? ( + {charts.length === 0 && !isLoading && !hasError ? ( No charts configured @@ -135,7 +166,7 @@ export const ImpactMetrics: FC = () => { variant='contained' startIcon={} onClick={handleAddChart} - disabled={metadataLoading || !!hasError} + disabled={isLoading || !!hasError} > Add Chart @@ -153,7 +184,7 @@ export const ImpactMetrics: FC = () => { onSave={handleSaveChart} initialConfig={editingChart} metricSeries={metricSeries} - loading={metadataLoading} + loading={metadataLoading || settingsLoading} /> ); diff --git a/frontend/src/component/impact-metrics/ImpactMetricsChart.tsx b/frontend/src/component/impact-metrics/ImpactMetricsChart.tsx index fc9b7f871a..27476e8829 100644 --- a/frontend/src/component/impact-metrics/ImpactMetricsChart.tsx +++ b/frontend/src/component/impact-metrics/ImpactMetricsChart.tsx @@ -1,6 +1,6 @@ import type { FC, ReactNode } from 'react'; import { useMemo } from 'react'; -import { Alert } from '@mui/material'; +import { Alert, Box, Typography } from '@mui/material'; import { LineChart, NotEnoughData, @@ -16,11 +16,13 @@ type ImpactMetricsChartProps = { selectedRange: 'hour' | 'day' | 'week' | 'month'; selectedLabels: Record; beginAtZero: boolean; + showRate?: boolean; aspectRatio?: number; overrideOptions?: Record; errorTitle?: string; emptyDataDescription?: string; noSeriesPlaceholder?: ReactNode; + isPreview?: boolean; }; export const ImpactMetricsChart: FC = ({ @@ -28,14 +30,16 @@ export const ImpactMetricsChart: FC = ({ selectedRange, selectedLabels, beginAtZero, + showRate, aspectRatio, overrideOptions = {}, - errorTitle = 'Failed to load impact metrics. Please check if Prometheus is configured and the feature flag is enabled.', + errorTitle = 'Failed to load impact metrics.', emptyDataDescription = 'Send impact metrics using Unleash SDK and select data series to view the chart.', noSeriesPlaceholder, + isPreview, }) => { const { - data: { start, end, series: timeSeriesData }, + data: { start, end, series: timeSeriesData, debug }, loading: dataLoading, error: dataError, } = useImpactMetricsData( @@ -43,6 +47,7 @@ export const ImpactMetricsChart: FC = ({ ? { series: selectedSeries, range: selectedRange, + showRate, labels: Object.keys(selectedLabels).length > 0 ? selectedLabels @@ -113,13 +118,14 @@ export const ImpactMetricsChart: FC = ({ y: { beginAtZero, title: { - display: false, + display: !!showRate, + text: showRate ? 'Rate per second' : '', }, ticks: { precision: 0, callback: (value: unknown): string | number => typeof value === 'number' - ? formatLargeNumbers(value) + ? `${formatLargeNumbers(value)}${showRate ? '/s' : ''}` : (value as number), }, }, @@ -143,13 +149,50 @@ export const ImpactMetricsChart: FC = ({ return ( <> - {hasError ? {errorTitle} : null} - + div': { + height: '100% !important', + width: '100% !important', + }, + } + : {} + } + > + {errorTitle} + ) : ( + cover + ) + } + /> + + {isPreview && debug?.query ? ( + ({ + margin: theme.spacing(2), + padding: theme.spacing(2), + background: theme.palette.background.elevation1, + })} + > + + {debug.query} + + + ) : null} ); }; diff --git a/frontend/src/component/impact-metrics/ImpactMetricsChartPreview.tsx b/frontend/src/component/impact-metrics/ImpactMetricsChartPreview.tsx index 087c9b80ee..fc07edd009 100644 --- a/frontend/src/component/impact-metrics/ImpactMetricsChartPreview.tsx +++ b/frontend/src/component/impact-metrics/ImpactMetricsChartPreview.tsx @@ -8,6 +8,7 @@ type ImpactMetricsChartPreviewProps = { selectedRange: 'hour' | 'day' | 'week' | 'month'; selectedLabels: Record; beginAtZero: boolean; + showRate?: boolean; }; export const ImpactMetricsChartPreview: FC = ({ @@ -15,6 +16,7 @@ export const ImpactMetricsChartPreview: FC = ({ selectedRange, selectedLabels, beginAtZero, + showRate, }) => ( <> @@ -33,6 +35,8 @@ export const ImpactMetricsChartPreview: FC = ({ selectedRange={selectedRange} selectedLabels={selectedLabels} beginAtZero={beginAtZero} + showRate={showRate} + isPreview /> diff --git a/frontend/src/component/impact-metrics/ImpactMetricsControls/ImpactMetricsControls.tsx b/frontend/src/component/impact-metrics/ImpactMetricsControls/ImpactMetricsControls.tsx index 1c166e9be2..907e0d5621 100644 --- a/frontend/src/component/impact-metrics/ImpactMetricsControls/ImpactMetricsControls.tsx +++ b/frontend/src/component/impact-metrics/ImpactMetricsControls/ImpactMetricsControls.tsx @@ -15,6 +15,7 @@ export type ImpactMetricsControlsProps = { | 'setSelectedRange' | 'setBeginAtZero' | 'setSelectedLabels' + | 'setShowRate' >; metricSeries: (ImpactMetricsSeries & { name: string })[]; loading?: boolean; @@ -54,16 +55,29 @@ export const ImpactMetricsControls: FC = ({ onChange={actions.setSelectedRange} /> - actions.setBeginAtZero(e.target.checked)} - /> - } - label='Begin at zero' - /> + + + actions.setBeginAtZero(e.target.checked) + } + /> + } + label='Begin at zero' + /> + actions.setShowRate(e.target.checked)} + /> + } + label='Show rate per second' + /> + {availableLabels && ( ; }; actions: { @@ -21,6 +22,7 @@ export type ChartFormState = { setSelectedSeries: (series: string) => void; setSelectedRange: (range: 'hour' | 'day' | 'week' | 'month') => void; setBeginAtZero: (beginAtZero: boolean) => void; + setShowRate: (showRate: boolean) => void; setSelectedLabels: (labels: Record) => void; handleSeriesChange: (series: string) => void; getConfigToSave: () => Omit; @@ -46,6 +48,7 @@ export const useChartFormState = ({ const [selectedLabels, setSelectedLabels] = useState< Record >(initialConfig?.selectedLabels || {}); + const [showRate, setShowRate] = useState(initialConfig?.showRate || false); const { data: { labels: currentAvailableLabels }, @@ -54,6 +57,7 @@ export const useChartFormState = ({ ? { series: selectedSeries, range: selectedRange, + showRate, } : undefined, ); @@ -65,12 +69,14 @@ export const useChartFormState = ({ setSelectedRange(initialConfig.selectedRange); setBeginAtZero(initialConfig.beginAtZero); setSelectedLabels(initialConfig.selectedLabels); + setShowRate(initialConfig.showRate || false); } else if (open && !initialConfig) { setTitle(''); setSelectedSeries(''); setSelectedRange('day'); setBeginAtZero(false); setSelectedLabels({}); + setShowRate(false); } }, [open, initialConfig]); @@ -85,6 +91,7 @@ export const useChartFormState = ({ selectedRange, beginAtZero, selectedLabels, + showRate, }); const isValid = selectedSeries.length > 0; @@ -95,6 +102,7 @@ export const useChartFormState = ({ selectedSeries, selectedRange, beginAtZero, + showRate, selectedLabels, }, actions: { @@ -102,6 +110,7 @@ export const useChartFormState = ({ setSelectedSeries, setSelectedRange, setBeginAtZero, + setShowRate, setSelectedLabels, handleSeriesChange, getConfigToSave, diff --git a/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx b/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx index 3ac0899420..f113df5347 100644 --- a/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx +++ b/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx @@ -1,120 +1,332 @@ +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { render } from 'utils/testRenderer'; +import { testServerRoute, testServerSetup } from 'utils/testServer'; import { useImpactMetricsState } from './useImpactMetricsState.ts'; -import { Route, Routes } from 'react-router-dom'; -import { createLocalStorage } from '../../../utils/createLocalStorage.ts'; import type { FC } from 'react'; import type { ImpactMetricsState } from '../types.ts'; -const TestComponent: FC = () => { - const { charts, layout } = useImpactMetricsState(); +const server = testServerSetup(); + +const TestComponent: FC<{ + enableActions?: boolean; +}> = ({ enableActions = false }) => { + const { + charts, + layout, + loading, + error, + addChart, + updateChart, + deleteChart, + } = useImpactMetricsState(); return (
{charts.length} {layout.length} + {loading.toString()} + {error ? 'has-error' : 'no-error'} + + {enableActions && ( + + )} + + {enableActions && charts.length > 0 && ( + + )} + + {enableActions && charts.length > 0 && ( + + )}
); }; -const TestWrapper = () => ( - - } /> - -); +const mockSettings: ImpactMetricsState = { + charts: [ + { + id: 'test-chart', + selectedSeries: 'test-series', + selectedRange: 'day' as const, + beginAtZero: true, + showRate: false, + selectedLabels: {}, + title: 'Test Chart', + }, + ], + layout: [ + { + i: 'test-chart', + x: 0, + y: 0, + w: 6, + h: 4, + minW: 4, + minH: 2, + maxW: 12, + maxH: 8, + }, + ], +}; + +const emptySettings: ImpactMetricsState = { + charts: [], + layout: [], +}; describe('useImpactMetricsState', () => { beforeEach(() => { - window.localStorage.clear(); + testServerRoute(server, '/api/admin/ui-config', {}); }); - it('loads state from localStorage to the URL after opening page without URL state', async () => { - const { setValue } = createLocalStorage( - 'impact-metrics-state', - { - charts: [], - layout: [], - }, + it('loads settings from API', async () => { + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + mockSettings, ); - setValue({ - charts: [ - { - id: 'test-chart', - selectedSeries: 'test-series', - selectedRange: 'day' as const, - beginAtZero: true, - selectedLabels: {}, - title: 'Test Chart', - }, - ], - layout: [ - { - i: 'test-chart', - x: 0, - y: 0, - w: 6, - h: 4, - minW: 4, - minH: 2, - maxW: 12, - maxH: 8, - }, - ], + render(); + + await waitFor(() => { + expect(screen.getByTestId('charts-count')).toHaveTextContent('1'); + expect(screen.getByTestId('layout-count')).toHaveTextContent('1'); + expect(screen.getByTestId('loading')).toHaveTextContent('false'); + expect(screen.getByTestId('error')).toHaveTextContent('no-error'); }); - - render(, { route: '/impact-metrics' }); - - expect(window.location.href).toContain('charts='); - expect(window.location.href).toContain('layout='); }); - it('does not modify URL when URL already contains data', async () => { - const { setValue } = createLocalStorage( - 'impact-metrics-state', + it('handles empty settings', async () => { + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + emptySettings, + ); + + render(); + + await waitFor(() => { + expect(screen.getByTestId('charts-count')).toHaveTextContent('0'); + expect(screen.getByTestId('layout-count')).toHaveTextContent('0'); + expect(screen.getByTestId('loading')).toHaveTextContent('false'); + expect(screen.getByTestId('error')).toHaveTextContent('no-error'); + }); + }); + + it('handles API errors', async () => { + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + { message: 'Server error' }, + 'get', + 500, + ); + + render(); + + await waitFor(() => { + expect(screen.getByTestId('error')).toHaveTextContent('has-error'); + }); + }); + + it('adds a chart successfully', async () => { + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + emptySettings, + ); + + render(); + + await waitFor(() => { + expect(screen.getByTestId('charts-count')).toHaveTextContent('0'); + }); + + testServerRoute( + server, + '/api/admin/impact-metrics/settings', { - charts: [], - layout: [], + charts: [ + { + id: 'new-chart-id', + selectedSeries: 'test-series', + selectedRange: 'day', + beginAtZero: true, + showRate: false, + selectedLabels: {}, + title: 'Test Chart', + }, + ], + layout: [ + { + i: 'new-chart-id', + x: 0, + y: 0, + w: 6, + h: 4, + minW: 4, + minH: 2, + maxW: 12, + maxH: 8, + }, + ], }, + 'put', + 200, ); - setValue({ - charts: [ - { - id: 'old-chart', - selectedSeries: 'old-series', - selectedRange: 'day' as const, - beginAtZero: true, - selectedLabels: {}, - title: 'Old Chart', - }, - ], - layout: [], - }); - - const urlCharts = btoa( - JSON.stringify([ - { - id: 'url-chart', - selectedSeries: 'url-series', - selectedRange: 'day', - beginAtZero: true, - selectedLabels: {}, - title: 'URL Chart', - }, - ]), + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + { + charts: [ + { + id: 'new-chart-id', + selectedSeries: 'test-series', + selectedRange: 'day', + beginAtZero: true, + showRate: false, + selectedLabels: {}, + title: 'Test Chart', + }, + ], + layout: [ + { + i: 'new-chart-id', + x: 0, + y: 0, + w: 6, + h: 4, + minW: 4, + minH: 2, + maxW: 12, + maxH: 8, + }, + ], + }, + 'get', + 200, ); - render(, { - route: `/impact-metrics?charts=${encodeURIComponent(urlCharts)}`, + const addButton = screen.getByTestId('add-chart'); + await userEvent.click(addButton); + + await waitFor( + () => { + expect(screen.getByTestId('charts-count')).toHaveTextContent( + '1', + ); + }, + { timeout: 5000 }, + ); + }); + + it('updates a chart successfully', async () => { + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + mockSettings, + ); + + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + { + charts: [ + { + ...mockSettings.charts[0], + title: 'Updated Chart', + }, + ], + layout: mockSettings.layout, + }, + 'put', + 200, + ); + + render(); + + await waitFor(() => { + expect(screen.getByTestId('charts-count')).toHaveTextContent('1'); }); - const urlParams = new URLSearchParams(window.location.search); - const chartsParam = urlParams.get('charts'); + const updateButton = screen.getByTestId('update-chart'); + await userEvent.click(updateButton); - expect(chartsParam).toBeTruthy(); + await waitFor(() => { + expect(screen.getByTestId('charts-count')).toHaveTextContent('1'); + }); + }); - const decodedCharts = JSON.parse(atob(chartsParam!)); - expect(decodedCharts[0].id).toBe('url-chart'); - expect(decodedCharts[0].id).not.toBe('old-chart'); + it('deletes a chart successfully', async () => { + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + mockSettings, + ); + + render(); + + await waitFor(() => { + expect(screen.getByTestId('charts-count')).toHaveTextContent('1'); + }); + + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + emptySettings, + 'put', + 200, + ); + + testServerRoute( + server, + '/api/admin/impact-metrics/settings', + emptySettings, + 'get', + 200, + ); + + const deleteButton = screen.getByTestId('delete-chart'); + await userEvent.click(deleteButton); + + await waitFor( + () => { + expect(screen.getByTestId('charts-count')).toHaveTextContent( + '0', + ); + }, + { timeout: 5000 }, + ); }); }); diff --git a/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.ts b/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.ts index 58069dba09..b2e8caec52 100644 --- a/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.ts +++ b/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.ts @@ -1,72 +1,40 @@ -import { useCallback, useMemo } from 'react'; -import { withDefault } from 'use-query-params'; -import { usePersistentTableState } from 'hooks/usePersistentTableState'; +import { useCallback } from 'react'; +import { useImpactMetricsSettings } from 'hooks/api/getters/useImpactMetricsSettings/useImpactMetricsSettings.js'; +import { useImpactMetricsSettingsApi } from 'hooks/api/actions/useImpactMetricsSettingsApi/useImpactMetricsSettingsApi.js'; import type { ChartConfig, ImpactMetricsState, LayoutItem } from '../types.ts'; -const createArrayParam = () => ({ - encode: (items: T[]): string => - items.length > 0 ? btoa(JSON.stringify(items)) : '', - - decode: (value: string | (string | null)[] | null | undefined): T[] => { - if (typeof value !== 'string' || !value) return []; - try { - return JSON.parse(atob(value)); - } catch { - return []; - } - }, -}); - -const ChartsParam = createArrayParam(); -const LayoutParam = createArrayParam(); - export const useImpactMetricsState = () => { - const stateConfig = { - charts: withDefault(ChartsParam, []), - layout: withDefault(LayoutParam, []), - }; + const { + settings, + loading: settingsLoading, + error: settingsError, + refetch, + } = useImpactMetricsSettings(); - const [tableState, setTableState] = usePersistentTableState( - 'impact-metrics-state', - stateConfig, - ); - - const currentState: ImpactMetricsState = useMemo( - () => ({ - charts: tableState.charts || [], - layout: tableState.layout || [], - }), - [tableState.charts, tableState.layout], - ); - - const updateState = useCallback( - (newState: ImpactMetricsState) => { - setTableState({ - charts: newState.charts, - layout: newState.layout, - }); - }, - [setTableState], - ); + const { + updateSettings, + loading: actionLoading, + errors: actionErrors, + } = useImpactMetricsSettingsApi(); const addChart = useCallback( - (config: Omit) => { + async (config: Omit) => { const newChart: ChartConfig = { ...config, id: `chart-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, }; const maxY = - currentState.layout.length > 0 + settings.layout.length > 0 ? Math.max( - ...currentState.layout.map((item) => item.y + item.h), + ...settings.layout.map((item) => item.y + item.h), ) : 0; - updateState({ - charts: [...currentState.charts, newChart], + const newState: ImpactMetricsState = { + charts: [...settings.charts, newChart], layout: [ - ...currentState.layout, + ...settings.layout, { i: newChart.id, x: 0, @@ -79,46 +47,61 @@ export const useImpactMetricsState = () => { maxH: 8, }, ], - }); + }; + + await updateSettings(newState); + refetch(); }, - [currentState.charts, currentState.layout, updateState], + [settings, updateSettings, refetch], ); const updateChart = useCallback( - (id: string, updates: Partial) => { - updateState({ - charts: currentState.charts.map((chart) => - chart.id === id ? { ...chart, ...updates } : chart, - ), - layout: currentState.layout, - }); + async (id: string, updates: Partial) => { + const updatedCharts = settings.charts.map((chart) => + chart.id === id ? { ...chart, ...updates } : chart, + ); + const newState: ImpactMetricsState = { + charts: updatedCharts, + layout: settings.layout, + }; + await updateSettings(newState); + refetch(); }, - [currentState.charts, currentState.layout, updateState], + [settings, updateSettings, refetch], ); const deleteChart = useCallback( - (id: string) => { - updateState({ - charts: currentState.charts.filter((chart) => chart.id !== id), - layout: currentState.layout.filter((item) => item.i !== id), - }); + async (id: string) => { + const newState: ImpactMetricsState = { + charts: settings.charts.filter((chart) => chart.id !== id), + layout: settings.layout.filter((item) => item.i !== id), + }; + await updateSettings(newState); + refetch(); }, - [currentState.charts, currentState.layout, updateState], + [settings, updateSettings, refetch], ); const updateLayout = useCallback( - (newLayout: LayoutItem[]) => { - updateState({ - charts: currentState.charts, + async (newLayout: LayoutItem[]) => { + const newState: ImpactMetricsState = { + charts: settings.charts, layout: newLayout, - }); + }; + await updateSettings(newState); + refetch(); }, - [currentState.charts, updateState], + [settings, updateSettings, refetch], ); return { - charts: currentState.charts || [], - layout: currentState.layout || [], + charts: settings.charts || [], + layout: settings.layout || [], + loading: settingsLoading || actionLoading, + error: + settingsError || Object.keys(actionErrors).length > 0 + ? actionErrors + : undefined, addChart, updateChart, deleteChart, diff --git a/frontend/src/component/impact-metrics/types.ts b/frontend/src/component/impact-metrics/types.ts index 177975ed78..e979ca58c7 100644 --- a/frontend/src/component/impact-metrics/types.ts +++ b/frontend/src/component/impact-metrics/types.ts @@ -3,6 +3,7 @@ export type ChartConfig = { selectedSeries: string; selectedRange: 'hour' | 'day' | 'week' | 'month'; beginAtZero: boolean; + showRate: boolean; selectedLabels: Record; title?: string; }; diff --git a/frontend/src/component/insights/InsightsCharts.tsx b/frontend/src/component/insights/InsightsCharts.tsx index fb244f4aba..ec29147ee8 100644 --- a/frontend/src/component/insights/InsightsCharts.tsx +++ b/frontend/src/component/insights/InsightsCharts.tsx @@ -18,7 +18,6 @@ import { allOption } from 'component/common/ProjectSelect/ProjectSelect'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { WidgetTitle } from './components/WidgetTitle/WidgetTitle.tsx'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { useUiFlag } from 'hooks/useUiFlag.ts'; export interface IChartsProps { flagTrends: InstanceInsightsSchema['flagTrends']; @@ -39,6 +38,7 @@ export interface IChartsProps { potentiallyStale: number; averageUsers: number; averageHealth?: string; + technicalDebt?: string; flagsPerUser?: string; medianTimeToProduction?: number; }; @@ -105,7 +105,6 @@ export const InsightsCharts: FC = ({ const showAllProjects = projects[0] === allOption.id; const isOneProjectSelected = projects.length === 1; const { isEnterprise } = useUiConfig(); - const healthToDebtEnabled = useUiFlag('healthToTechDebt'); const lastUserTrend = userTrends[userTrends.length - 1]; const lastFlagTrend = flagTrends[flagTrends.length - 1]; @@ -186,20 +185,15 @@ export const InsightsCharts: FC = ({ } diff --git a/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx b/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx index 605e6410d6..c1891fe02b 100644 --- a/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx +++ b/frontend/src/component/insights/componentsChart/ProjectHealthChart/HealthChartTooltip/HealthChartTooltip.tsx @@ -6,7 +6,6 @@ import type { TooltipState } from 'component/insights/components/LineChart/Chart import { HorizontalDistributionChart } from 'component/insights/components/HorizontalDistributionChart/HorizontalDistributionChart'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { getTechnicalDebtColor } from 'utils/getTechnicalDebtColor.ts'; -import { useUiFlag } from 'hooks/useUiFlag'; const StyledTooltipItemContainer = styled(Paper)(({ theme }) => ({ padding: theme.spacing(2), @@ -19,22 +18,6 @@ const StyledItemHeader = styled(Box)(({ theme }) => ({ alignItems: 'center', })); -const getHealthBadgeColor = (health?: number | null) => { - if (health === undefined || health === null) { - return 'info'; - } - - if (health >= 75) { - return 'success'; - } - - if (health >= 50) { - return 'warning'; - } - - return 'error'; -}; - const getTechnicalDebtBadgeColor = (technicalDebt?: number | null) => { if (technicalDebt === undefined || technicalDebt === null) { return 'info'; @@ -108,8 +91,6 @@ const Distribution = ({ stale = 0, potentiallyStale = 0, total = 0 }) => { export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({ tooltip, }) => { - const healthToTechDebtEnabled = useUiFlag('healthToTechDebt'); - const data = tooltip?.dataPoints.map((point) => { return { label: point.label, @@ -148,9 +129,7 @@ export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({ color='textSecondary' component='span' > - {healthToTechDebtEnabled - ? 'Technical debt' - : 'Project health'} + Technical debt
@@ -163,21 +142,13 @@ export const HealthTooltip: FC<{ tooltip: TooltipState | null }> = ({
{point.title} - {healthToTechDebtEnabled ? ( - - {point.value.technicalDebt}% - - ) : ( - - {point.value.health}% - - )} + + {point.value.technicalDebt}% + {' '} ({ margin: theme.spacing(1.5, 0) })} diff --git a/frontend/src/component/insights/componentsChart/ProjectHealthChart/ProjectHealthChart.tsx b/frontend/src/component/insights/componentsChart/ProjectHealthChart/ProjectHealthChart.tsx index 22bae055ed..9758f660de 100644 --- a/frontend/src/component/insights/componentsChart/ProjectHealthChart/ProjectHealthChart.tsx +++ b/frontend/src/component/insights/componentsChart/ProjectHealthChart/ProjectHealthChart.tsx @@ -14,7 +14,6 @@ import { import { useTheme } from '@mui/material'; import type { GroupedDataByProject } from 'component/insights/hooks/useGroupedProjectTrends'; import { usePlaceholderData } from 'component/insights/hooks/usePlaceholderData'; -import { useUiFlag } from 'hooks/useUiFlag.ts'; interface IProjectHealthChartProps { projectFlagTrends: GroupedDataByProject< @@ -46,7 +45,6 @@ export const ProjectHealthChart: FC = ({ const projectsData = useProjectChartData(projectFlagTrends); const theme = useTheme(); const placeholderData = usePlaceholderData(); - const healthToTechDebtEnabled = useUiFlag('healthToTechDebt'); const aggregateHealthData = useMemo(() => { const labels = Array.from( @@ -85,18 +83,12 @@ export const ProjectHealthChart: FC = ({ return { datasets: [ { - label: healthToTechDebtEnabled - ? 'Technical debt' - : 'Health', + label: 'Technical debt', data: weeks.map((item) => ({ health: item.total ? calculateHealth(item) : undefined, - ...(healthToTechDebtEnabled - ? { - technicalDebt: item.total - ? calculateTechDebt(item) - : undefined, - } - : {}), + technicalDebt: item.total + ? calculateTechDebt(item) + : undefined, date: item.date, total: item.total, stale: item.stale, @@ -132,9 +124,7 @@ export const ProjectHealthChart: FC = ({ ? {} : { parsing: { - yAxisKey: healthToTechDebtEnabled - ? 'technicalDebt' - : 'health', + yAxisKey: 'technicalDebt', xAxisKey: 'date', }, plugins: { diff --git a/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx b/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx index 6f0aaddbb6..506ca47a18 100644 --- a/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx +++ b/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx @@ -1,7 +1,6 @@ import type { FC, ReactNode } from 'react'; import { Box, Divider, Link, styled } from '@mui/material'; import { ReactComponent as InstanceHealthIcon } from 'assets/icons/instance-health.svg'; -import { useUiFlag } from 'hooks/useUiFlag'; interface IHealthStatsProps { value?: string | number; @@ -73,8 +72,6 @@ export const HealthStats: FC = ({ potentiallyStale, title, }) => { - const healthToDebtEnabled = useUiFlag('healthToTechDebt'); - return ( @@ -84,12 +81,8 @@ export const HealthStats: FC = ({ - {healthToDebtEnabled ? 'Technical debt' : 'Instance health'} - {healthToDebtEnabled ? ( - {`${technicalDebt}%`} - ) : ( - {`${value || 0}%`} - )} + Technical debt + {`${technicalDebt}%`} @@ -112,9 +105,7 @@ export const HealthStats: FC = ({ target='_blank' rel='noreferrer' > - {healthToDebtEnabled - ? 'What affects technical debt?' - : 'What affects instance health?'} + What affects technical debt? diff --git a/frontend/src/component/insights/sections/PerformanceInsights.tsx b/frontend/src/component/insights/sections/PerformanceInsights.tsx index d7687148a5..bae9edf399 100644 --- a/frontend/src/component/insights/sections/PerformanceInsights.tsx +++ b/frontend/src/component/insights/sections/PerformanceInsights.tsx @@ -23,7 +23,6 @@ import { StyledWidgetContent, StyledWidgetStats, } from '../InsightsCharts.styles'; -import { useUiFlag } from 'hooks/useUiFlag'; export const PerformanceInsights: FC = () => { const statePrefix = 'performance-'; @@ -48,8 +47,6 @@ export const PerformanceInsights: FC = () => { state[`${statePrefix}to`]?.values[0], ); - const healthToTechDebtEnabled = useUiFlag('healthToTechDebt'); - const projects = state[`${statePrefix}project`]?.values ?? [allOption.id]; const showAllProjects = projects[0] === allOption.id; @@ -135,21 +132,12 @@ export const PerformanceInsights: FC = () => { stale={summary.stale} potentiallyStale={summary.potentiallyStale} title={ - healthToTechDebtEnabled ? ( - - ) : ( - - ) + } /> diff --git a/frontend/src/component/personalDashboard/MyProjects.tsx b/frontend/src/component/personalDashboard/MyProjects.tsx index 4389f24542..99df545666 100644 --- a/frontend/src/component/personalDashboard/MyProjects.tsx +++ b/frontend/src/component/personalDashboard/MyProjects.tsx @@ -39,13 +39,10 @@ import { ActionBox } from './ActionBox.tsx'; import useLoading from 'hooks/useLoading'; import { NoProjectsContactAdmin } from './NoProjectsContactAdmin.tsx'; import { AskOwnerToAddYouToTheirProject } from './AskOwnerToAddYouToTheirProject.tsx'; -import { useUiFlag } from 'hooks/useUiFlag.ts'; const ActiveProjectDetails: FC<{ project: PersonalDashboardSchemaProjectsItem; }> = ({ project }) => { - const healthToTechDebtEnabled = useUiFlag('healthToTechDebt'); - const techicalDebt = project.technicalDebt; return ( @@ -67,10 +64,10 @@ const ActiveProjectDetails: FC<{ - {healthToTechDebtEnabled ? techicalDebt : project.health}% + {techicalDebt}% - {healthToTechDebtEnabled ? 'technical debt' : 'health'} + technical debt diff --git a/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx b/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx index ec1cba7df7..4b249b0783 100644 --- a/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx +++ b/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx @@ -18,7 +18,7 @@ const setupLongRunningProject = () => { id: 'projectId', memberCount: 10, featureCount: 100, - health: 80, + techicalDebt: 20, name: 'projectName', }, ], @@ -40,7 +40,7 @@ const setupLongRunningProject = () => { potentiallyStaleFlags: 14, staleFlags: 13, activeFlags: 12, - health: 81, + technicalDebt: 19, }, latestEvents: [{ summary: 'someone created a flag', id: 0 }], roles: [{ name: 'Member' }], @@ -84,7 +84,7 @@ const setupNewProject = () => { id: 'projectId', memberCount: 3, featureCount: 0, - health: 100, + technicalDebt: 0, name: 'projectName', }, ], @@ -142,10 +142,10 @@ test('Render personal dashboard for a long running project', async () => { await screen.findByText('projectName'); await screen.findByText('10'); // members await screen.findByText('100'); // features - await screen.findAllByText('80%'); // health + await screen.findAllByText('20%'); // technical debt - await screen.findByText('Project health'); - await screen.findByText('70%'); // avg health past window + await screen.findByText('Technical debt'); + await screen.findByText('30%'); // avg technical debt past window await screen.findByText('someone created a flag'); await screen.findByText('Member'); await screen.findByText('myFlag'); @@ -161,7 +161,7 @@ test('Render personal dashboard for a new project', async () => { await screen.findByText('projectName'); await screen.findByText('3'); // members await screen.findByText('0'); // features - await screen.findByText('100%'); // health + await screen.findByText('0%'); // technical debt await screen.findByText('Create a feature flag'); await screen.findByText('Connect an SDK'); diff --git a/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx b/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx index c171d912b3..006965c886 100644 --- a/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx +++ b/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx @@ -4,7 +4,6 @@ import { Link } from 'react-router-dom'; import Lightbulb from '@mui/icons-material/LightbulbOutlined'; import type { PersonalDashboardProjectDetailsSchemaInsights } from 'openapi'; import { ActionBox } from './ActionBox.tsx'; -import { useUiFlag } from 'hooks/useUiFlag.ts'; const PercentageScore = styled('span')(({ theme }) => ({ fontWeight: theme.typography.fontWeightBold, @@ -58,42 +57,24 @@ const ProjectHealthMessage: FC<{ insights: PersonalDashboardProjectDetailsSchemaInsights; project: string; }> = ({ trend, insights, project }) => { - const healthToTechDebtEnabled = useUiFlag('healthToTechDebt'); - const { avgHealthCurrentWindow, avgHealthPastWindow, health } = insights; - const improveMessage = healthToTechDebtEnabled - ? 'Remember to archive your stale feature flags to keep the technical debt low.' - : 'Remember to archive your stale feature flags to keep the project health growing.'; + const { avgHealthCurrentWindow, avgHealthPastWindow } = insights; + const improveMessage = + 'Remember to archive your stale feature flags to keep the technical debt low.'; const keepDoingMessage = 'This indicates that you are doing a good job of archiving your feature flags.'; const avgCurrentTechnicalDebt = 100 - (avgHealthCurrentWindow ?? 0); const avgPastTechnicalDebt = 100 - (avgHealthPastWindow ?? 0); if (trend === 'improved') { - if (healthToTechDebtEnabled) { - return ( - <> - - On average, your project technical debt went down from{' '} - - {avgPastTechnicalDebt}% - {' '} - to{' '} - - {avgCurrentTechnicalDebt}% - {' '} - during the last 4 weeks. - - {keepDoingMessage} - - ); - } - return ( <> - On average, your project health went up from{' '} - {avgHealthPastWindow}% to{' '} - {avgHealthCurrentWindow}%{' '} + On average, your project technical debt went down from{' '} + {avgPastTechnicalDebt}%{' '} + to{' '} + + {avgCurrentTechnicalDebt}% + {' '} during the last 4 weeks. {keepDoingMessage} @@ -102,31 +83,15 @@ const ProjectHealthMessage: FC<{ } if (trend === 'declined') { - if (healthToTechDebtEnabled) { - return ( - <> - - On average, your project technical debt went up from{' '} - - {avgPastTechnicalDebt}% - {' '} - to{' '} - - {avgCurrentTechnicalDebt}% - {' '} - during the last 4 weeks. - - {improveMessage} - - ); - } - return ( <> - On average, your project health went down from{' '} - {avgHealthPastWindow}% to{' '} - {avgHealthCurrentWindow}%{' '} + On average, your project technical debt went up from{' '} + {avgPastTechnicalDebt}%{' '} + to{' '} + + {avgCurrentTechnicalDebt}% + {' '} during the last 4 weeks. {improveMessage} @@ -135,62 +100,31 @@ const ProjectHealthMessage: FC<{ } if (trend === 'consistent') { - if (healthToTechDebtEnabled) { - return ( - <> - - On average, your project technical debt has remained at{' '} - - {avgCurrentTechnicalDebt}% - {' '} - during the last 4 weeks. - - {keepDoingMessage} - - ); - } - return ( <> - On average, your project health has remained at{' '} - {avgHealthCurrentWindow}%{' '} + On average, your project technical debt has remained at{' '} + + {avgCurrentTechnicalDebt}% + {' '} during the last 4 weeks. - - {avgHealthCurrentWindow && avgHealthCurrentWindow >= 75 - ? keepDoingMessage - : improveMessage} - + {keepDoingMessage} ); } if (trend === 'unknown') { - if (healthToTechDebtEnabled) { - return ( - <> - - Your current project technical debt is{' '} - - {avgCurrentTechnicalDebt}% - - . - - {improveMessage} - - ); - } - return ( <> - Your current health score is{' '} - {health}%. - - - {health >= 75 ? keepDoingMessage : improveMessage} + Your current project technical debt is{' '} + + {avgCurrentTechnicalDebt}% + + . + {improveMessage} ); } @@ -202,7 +136,6 @@ export const ProjectSetupComplete: FC<{ project: string; insights: PersonalDashboardProjectDetailsSchemaInsights; }> = ({ project, insights }) => { - const healthToTechDebtEnabled = useUiFlag('healthToTechDebt'); const projectHealthTrend = determineProjectHealthTrend(insights); return ( @@ -211,9 +144,7 @@ export const ProjectSetupComplete: FC<{ <> - {healthToTechDebtEnabled - ? 'Technical debt' - : 'Project health'} + Technical debt } diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx index c5c77363d0..7d4af5dedf 100644 --- a/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectHealth.tsx @@ -6,7 +6,6 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { HealthGridTile } from './ProjectHealthGrid.styles'; import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber'; import { getTechnicalDebtColor } from 'utils/getTechnicalDebtColor.ts'; -import { useUiFlag } from 'hooks/useUiFlag'; const ChartRadius = 40; const ChartStrokeWidth = 13; @@ -82,17 +81,6 @@ const UnhealthyFlagBox = ({ flagCount }: { flagCount: number }) => { ); }; -const useHealthColor = (healthRating: number) => { - const theme = useTheme(); - if (healthRating <= 24) { - return theme.palette.error.main; - } - if (healthRating <= 74) { - return theme.palette.warning.border; - } - return theme.palette.success.border; -}; - const useTechnicalDebtColor = (techicalDebt: number) => { const theme = useTheme(); switch (getTechnicalDebtColor(techicalDebt)) { @@ -115,22 +103,18 @@ const Wrapper = styled(HealthGridTile)(({ theme }) => ({ export const ProjectHealth = () => { const projectId = useRequiredPathParam('projectId'); const { - data: { health, technicalDebt, staleFlags }, + data: { technicalDebt, staleFlags }, } = useProjectStatus(projectId); const { isOss } = useUiConfig(); const theme = useTheme(); - const healthToDebtEnabled = useUiFlag('healthToTechDebt'); const circumference = 2 * Math.PI * ChartRadius; - const healthRating = health.current; const gapLength = 0.3; const filledLength = 1 - gapLength; const offset = 0.75 - gapLength / 2; - const healthLength = (healthRating / 100) * circumference * 0.7; const technicalDebtLength = ((technicalDebt.current || 0) / 100) * circumference * 0.7; - const healthColor = useHealthColor(healthRating); const technicalDebtColor = useTechnicalDebtColor( technicalDebt.current || 0, ); @@ -155,17 +139,9 @@ export const ProjectHealth = () => { cy='50' r={ChartRadius} fill='none' - stroke={ - healthToDebtEnabled - ? technicalDebtColor - : healthColor - } + stroke={technicalDebtColor} strokeWidth={ChartStrokeWidth} - strokeDasharray={ - healthToDebtEnabled - ? `${technicalDebtLength} ${circumference - technicalDebtLength}` - : `${healthLength} ${circumference - healthLength}` - } + strokeDasharray={`${technicalDebtLength} ${circumference - technicalDebtLength}`} strokeDashoffset={offset * circumference} /> { fill={theme.palette.text.primary} fontSize={theme.typography.h1.fontSize} > - {healthToDebtEnabled - ? technicalDebt.current || 0 - : healthRating} - % + {technicalDebt.current || 0}% - {healthToDebtEnabled ? ( - <> - Your current technical debt rating is{' '} - {technicalDebt.current}%. - - ) : ( - <> - Your current project health rating is{' '} - {healthRating}%. - - )} + Your current technical debt rating is{' '} + {technicalDebt.current}%. {!isOss() && ( - {healthToDebtEnabled - ? 'View technical debt over time' - : 'View health over time'} + View technical debt over time )} diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx index 83e85ad958..c4de76b103 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx @@ -351,7 +351,7 @@ export const ProjectAccessAssign = ({ modal title={`${!edit ? 'Assign' : 'Edit'} ${entityType} access`} description='Custom project roles allow you to fine-tune access rights and permissions within your projects.' - documentationLink='https://docs.getunleash.io/how-to/how-to-create-and-assign-custom-project-roles' + documentationLink='https://docs.getunleash.io/reference/rbac#create-and-assign-a-custom-project-role' documentationLinkLabel='Project access documentation' formatApiCode={formatApiCode} > diff --git a/frontend/src/hooks/api/actions/useImpactMetricsSettingsApi/useImpactMetricsSettingsApi.ts b/frontend/src/hooks/api/actions/useImpactMetricsSettingsApi/useImpactMetricsSettingsApi.ts new file mode 100644 index 0000000000..a0e562313d --- /dev/null +++ b/frontend/src/hooks/api/actions/useImpactMetricsSettingsApi/useImpactMetricsSettingsApi.ts @@ -0,0 +1,32 @@ +import { useCallback } from 'react'; +import useAPI from '../useApi/useApi.js'; +import type { ImpactMetricsState } from 'component/impact-metrics/types.ts'; + +export const useImpactMetricsSettingsApi = () => { + const { makeRequest, createRequest, errors, loading } = useAPI({ + propagateErrors: true, + }); + + const updateSettings = useCallback( + async (settings: ImpactMetricsState) => { + const path = `api/admin/impact-metrics/settings`; + const req = createRequest( + path, + { + method: 'PUT', + body: JSON.stringify(settings), + }, + 'updateImpactMetricsSettings', + ); + + return makeRequest(req.caller, req.id); + }, + [makeRequest, createRequest], + ); + + return { + updateSettings, + errors, + loading, + }; +}; diff --git a/frontend/src/hooks/api/getters/useImpactMetricsData/useImpactMetricsData.ts b/frontend/src/hooks/api/getters/useImpactMetricsData/useImpactMetricsData.ts index 930611435b..737d62a44b 100644 --- a/frontend/src/hooks/api/getters/useImpactMetricsData/useImpactMetricsData.ts +++ b/frontend/src/hooks/api/getters/useImpactMetricsData/useImpactMetricsData.ts @@ -16,12 +16,16 @@ export type ImpactMetricsResponse = { step?: string; series: ImpactMetricsSeries[]; labels?: ImpactMetricsLabels; + debug?: { + query?: string; + }; }; export type ImpactMetricsQuery = { series: string; range: 'hour' | 'day' | 'week' | 'month'; labels?: Record; + showRate?: boolean; }; export const useImpactMetricsData = (query?: ImpactMetricsQuery) => { @@ -34,6 +38,10 @@ export const useImpactMetricsData = (query?: ImpactMetricsQuery) => { range: query.range, }); + if (query.showRate !== undefined) { + params.append('showRate', query.showRate.toString()); + } + if (query.labels && Object.keys(query.labels).length > 0) { // Send labels as they are - the backend will handle the formatting const labelsParam = Object.entries(query.labels).reduce( diff --git a/frontend/src/hooks/api/getters/useImpactMetricsSettings/useImpactMetricsSettings.ts b/frontend/src/hooks/api/getters/useImpactMetricsSettings/useImpactMetricsSettings.ts new file mode 100644 index 0000000000..6847abc294 --- /dev/null +++ b/frontend/src/hooks/api/getters/useImpactMetricsSettings/useImpactMetricsSettings.ts @@ -0,0 +1,18 @@ +import { fetcher, useApiGetter } from '../useApiGetter/useApiGetter.js'; +import { formatApiPath } from 'utils/formatPath'; +import type { ImpactMetricsState } from 'component/impact-metrics/types.ts'; + +export const useImpactMetricsSettings = () => { + const PATH = `api/admin/impact-metrics/settings`; + const { data, refetch, loading, error } = useApiGetter( + formatApiPath(PATH), + () => fetcher(formatApiPath(PATH), 'Impact metrics settings'), + ); + + return { + settings: data || { charts: [], layout: [] }, + refetch, + loading, + error, + }; +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 250f8be9b6..c8d7fdd947 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -87,7 +87,6 @@ export type UiFlags = { customMetrics?: boolean; lifecycleMetrics?: boolean; createFlagDialogCache?: boolean; - healthToTechDebt?: boolean; improvedJsonDiff?: boolean; impactMetrics?: boolean; crDiffView?: boolean; diff --git a/package.json b/package.json index 5712eef4c9..ab6577598b 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "ts-toolbelt": "^9.6.0", "type-is": "^2.0.0", "ulidx": "^2.4.1", - "unleash-client": "^6.7.0-beta.1", + "unleash-client": "^6.7.0-beta.3", "uuid": "^11.0.0" }, "devDependencies": { diff --git a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts index f4998508df..7d51b1a7b4 100644 --- a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts +++ b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts @@ -215,7 +215,7 @@ export default class ClientMetricsServiceV2 { const invalidToggleNames = toggleNames.length - validatedToggleNames.length; - this.logger.info( + this.logger.debug( `Got ${toggleNames.length} (${invalidToggleNames > 0 ? `${invalidToggleNames} invalid ones` : 'all valid'}) metrics from ${value.appName}`, ); @@ -244,7 +244,7 @@ export default class ClientMetricsServiceV2 { seenAt: value.bucket.stop, environment, })); - this.logger.info( + this.logger.debug( `Registering ${unknownFlags.length} unknown flags from ${value.appName} in the ${environment} environment. Some of the unknown flag names include: ${unknownFlags .slice(0, 10) .map(({ name }) => `"${name}"`) diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 4cea036d57..9eb6efd27c 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -56,7 +56,6 @@ export type IFlagKey = | 'edgeObservability' | 'reportUnknownFlags' | 'lifecycleMetrics' - | 'healthToTechDebt' | 'customMetrics' | 'impactMetrics' | 'createFlagDialogCache' @@ -269,10 +268,6 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_LIFECYCLE_METRICS, false, ), - healthToTechDebt: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_HEALTH_TO_TECH_DEBT, - false, - ), createFlagDialogCache: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_CREATE_FLAG_DIALOG_CACHE, false, diff --git a/website/docs/api-overview.mdx b/website/docs/api-overview.mdx new file mode 100644 index 0000000000..acc9ffcfcc --- /dev/null +++ b/website/docs/api-overview.mdx @@ -0,0 +1,62 @@ +--- +title: API overview +description: "An overview of the three main Unleash APIs: Client API, Frontend API, and Admin API." +displayed_sidebar: documentation +--- + +## Unleash APIs + +Unleash provides a set of APIs to give you full programmatic control over your feature flags and to connect your applications and services to Unleash. There are three main APIs, each designed for a specific purpose. + +| API | Used by | Primary use case | +|---------------|---------|---| +| **Client API** | Server-side SDKs | Fetch feature flag configurations. | +| **Frontend API** | Client-side SDKs | Fetch enabled feature flags for a specific [Unleash Context](/reference/unleash-context). | +| **Admin API** | [Admin UI](#the-unleash-admin-ui), internal tooling, and third-party [integrations](/reference/integrations) | Access and manage all resources within Unleash, such as context, environments, events, metrics, and users. | + +## API authentication and tokens + +All Unleash APIs require authentication using an [API token](/reference/api-tokens-and-client-keys). The type of token you use depends on the API you are accessing and your specific use case. + +### Token types + +Unleash supports four types of API tokens: +- **Client tokens**: Used to connect server-side SDKs, Unleash Edge, and the Unleash Proxy to the Client API. Can be scoped to a specific project and environment. +- **Frontend tokens**: Used to connect client-side SDKs to the Frontend API or Unleash Edge. These tokens are designed to be publicly accessible and have limited permissions. Can be scoped to a specific project and environment. +- **Personal access tokens**: Tied to a specific user account. They are useful for testing, debugging, or providing temporary access to tools and scripts that need to interact with the Admin API. +- **Service account tokens**: The recommended method for providing API access to integrations, automation tools, and other non-human users. Service accounts provide a more secure and manageable way to grant Admin API access. + +For an end-to-end Unleash integration, you might need to use multiple token types. For example, when connecting a client-side SDK to Unleash using Unleash Edge, you'll need the following tokens: +- A frontend token for the client-side SDK to securely communicate with Unleash Edge. +- A client token for Unleash Edge to communicate with the main Unleash server. + +![Diagram showing the types of tokens needed to connect a client-side SDK with Edge, and Edge with Unleash](/img/token-types-example.png) + +Ensure that the client token has access to the same project and environment (or a broader scope) as the frontend token. + +### Create an API token + +Depending on your permissions, you can create API tokens in the Unleash Admin UI in four ways: + +- **Admin settings > Access control > API access**: for client or frontend tokens; requires the Admin root role, or a custom root role with API token permissions. +- **Admin settings > Service accounts > New service account**: for creating a service account and assigning a token. +- **Settings > API access** [inside a project]: for project-specific client or frontend tokens; permitted for project Members or users with a corresponding root role. +- **Profile > View profile settings > Personal API tokens**: for personal access tokens. + +## API specification + +For a comprehensive and interactive reference of all available endpoints, Unleash provides an OpenAPI specification. This is useful for exploring the APIs, generating client libraries, and for testing. + +:::note Availability + +**Unleash version**: `5.2+` enabled by default. +**Unleash version**: `4.13+` can be enabled using the `ENABLE_OAS` [environment variable](using-unleash/deploy/configuring-unleash). +::: + +You can access the specification from your Unleash instance at the following paths: + +- **Interactive Swagger UI**: `/docs/openapi/` +- **Raw JSON specification**: `/docs/openapi.json` + +For detailed guides on each API, please refer to the full reference documentation. + diff --git a/website/docs/feature-flag-tutorials/django/implementing-feature-flags-django.md b/website/docs/feature-flag-tutorials/django/implementing-feature-flags-django.md index 6825b5fe42..45fc322cda 100644 --- a/website/docs/feature-flag-tutorials/django/implementing-feature-flags-django.md +++ b/website/docs/feature-flag-tutorials/django/implementing-feature-flags-django.md @@ -18,7 +18,7 @@ For this tutorial, you'll need the following: ![architecture diagram for our implementation](../rails/diagram.png) -The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. For server-side applications or automated scripts, Unleash exposes an [API](/reference/api/unleash) defined by an OpenAPI specification, allowing you to perform these actions programmatically. +The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. For server-side applications or automated scripts, Unleash exposes an [API](/api-overview) defined by an OpenAPI specification, allowing you to perform these actions programmatically. ## 1. Install a local feature flag provider diff --git a/website/docs/feature-flag-tutorials/dotnet/implementing-feature-flags-dotnet.md b/website/docs/feature-flag-tutorials/dotnet/implementing-feature-flags-dotnet.md index 91c4142279..46daba54c0 100644 --- a/website/docs/feature-flag-tutorials/dotnet/implementing-feature-flags-dotnet.md +++ b/website/docs/feature-flag-tutorials/dotnet/implementing-feature-flags-dotnet.md @@ -24,7 +24,7 @@ For this tutorial, you’ll need the following: ![architecture diagram for our implementation](./diagram.png) -The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](https://docs.getunleash.io/reference/api/unleash) to perform the same actions straight from your CLI or app. +The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](/api-overview) to perform the same actions straight from your CLI or app. ## 1. Install a local feature flag provider diff --git a/website/docs/feature-flag-tutorials/golang/implementing-feature-flags-golang.md b/website/docs/feature-flag-tutorials/golang/implementing-feature-flags-golang.md index 63d8dd6ff4..bfdd20c634 100644 --- a/website/docs/feature-flag-tutorials/golang/implementing-feature-flags-golang.md +++ b/website/docs/feature-flag-tutorials/golang/implementing-feature-flags-golang.md @@ -21,7 +21,7 @@ For this tutorial, you'll need the following: ![architecture diagram for our implementation](./diagram.png) -The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. You can perform the same actions straight from your CLI or server-side app using the [Unleash API](/reference/api/unleash). +The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. You can perform the same actions straight from your CLI or server-side app using the [Unleash API](/api-overview). ## Best practices for back-end apps with Unleash diff --git a/website/docs/feature-flag-tutorials/java/implementing-feature-flags.mdx b/website/docs/feature-flag-tutorials/java/implementing-feature-flags.mdx index 900bfbcceb..69f307b97d 100644 --- a/website/docs/feature-flag-tutorials/java/implementing-feature-flags.mdx +++ b/website/docs/feature-flag-tutorials/java/implementing-feature-flags.mdx @@ -37,7 +37,7 @@ In this tutorial, you will need the following: This architecture diagram breaks down how the Java app works with Unleash to control feature flags. We connect the Unleash service to your Java app using the Java SDK. -The Unleash Server acts as a Feature Flag Control Service, managing and storing your feature flags. It enables the retrieval of flag data and, particularly when not utilizing a user interface, supports the sending of data to and from the service. The Unleash Server has a UI for creating and managing projects and feature flags. There are also [API commands available](https://docs.getunleash.io/reference/api/unleash) to perform the same actions straight from your CLI or server-side app. +The Unleash Server acts as a Feature Flag Control Service, managing and storing your feature flags. It enables the retrieval of flag data and, particularly when not utilizing a user interface, supports the sending of data to and from the service. The Unleash Server has a UI for creating and managing projects and feature flags. There are also [API commands available](/api-overview) to perform the same actions straight from your CLI or server-side app. ## 1. Feature Flag best practices for backend apps diff --git a/website/docs/feature-flag-tutorials/java/spring-boot-implementing-feature-flags.mdx b/website/docs/feature-flag-tutorials/java/spring-boot-implementing-feature-flags.mdx index 2108f851f5..1149fb6dcb 100644 --- a/website/docs/feature-flag-tutorials/java/spring-boot-implementing-feature-flags.mdx +++ b/website/docs/feature-flag-tutorials/java/spring-boot-implementing-feature-flags.mdx @@ -38,7 +38,7 @@ In this tutorial, you will need the following: This architecture diagram breaks down how the Java Spring Boot app works with Unleash to use feature flags. -The Unleash Server is a Feature Flag Control Service for managing and storing your feature flags. It enables the retrieval of flag data and, particularly when not utilizing a user interface, supports sending data to and from the service. The Unleash Server has a UI for creating and managing projects and feature flags. API commands are also [available](/reference/api/unleash) to perform the same actions from your CLI or server-side app. +The Unleash Server is a Feature Flag Control Service for managing and storing your feature flags. It enables the retrieval of flag data and, particularly when not utilizing a user interface, supports sending data to and from the service. The Unleash Server has a UI for creating and managing projects and feature flags. API commands are also [available](/api-overview) to perform the same actions from your CLI or server-side app. The Spring Boot SDK is an extension of the Java SDK, configured for Spring Boot-specific architecture and conventions. diff --git a/website/docs/feature-flag-tutorials/python/implementing-feature-flags.mdx b/website/docs/feature-flag-tutorials/python/implementing-feature-flags.mdx index 9a96c2b84f..20b9c7c7f4 100644 --- a/website/docs/feature-flag-tutorials/python/implementing-feature-flags.mdx +++ b/website/docs/feature-flag-tutorials/python/implementing-feature-flags.mdx @@ -43,7 +43,7 @@ In this tutorial, you will need the following: This architecture diagram breaks down how the Python app works with Unleash to control feature flags. We connect the Unleash service to your Python app using the Python SDK. -The Unleash Server is a **Feature Flag Control Service**, which is a service that manages your feature flags and is used to retrieve flag data from (and send data to, especially when not using a UI). The Unleash server has a UI for creating and managing projects and feature flags. There are also [API commands available](https://docs.getunleash.io/reference/api/unleash) to perform the same actions straight from your CLI or server-side app. +The Unleash Server is a **Feature Flag Control Service**, which is a service that manages your feature flags and is used to retrieve flag data from (and send data to, especially when not using a UI). The Unleash server has a UI for creating and managing projects and feature flags. There are also [API commands available](/api-overview) to perform the same actions straight from your CLI or server-side app. ## 1. Unleash best practice for backend apps diff --git a/website/docs/feature-flag-tutorials/rails/implementing-feature-flags-rails.md b/website/docs/feature-flag-tutorials/rails/implementing-feature-flags-rails.md index 9b983f0dd8..0b58481e88 100644 --- a/website/docs/feature-flag-tutorials/rails/implementing-feature-flags-rails.md +++ b/website/docs/feature-flag-tutorials/rails/implementing-feature-flags-rails.md @@ -30,7 +30,7 @@ For this tutorial, you’ll need the following: ![architecture diagram for our implementation](./diagram.png) -The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](/reference/api/unleash) to perform the same actions straight from your CLI or server-side app. +The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](/api-overview) to perform the same actions straight from your CLI or server-side app. ## 1. Best practices for back-end apps with Unleash diff --git a/website/docs/feature-flag-tutorials/react/implementing-feature-flags.mdx b/website/docs/feature-flag-tutorials/react/implementing-feature-flags.mdx index 57cacfbaa0..df6f40a916 100644 --- a/website/docs/feature-flag-tutorials/react/implementing-feature-flags.mdx +++ b/website/docs/feature-flag-tutorials/react/implementing-feature-flags.mdx @@ -1,6 +1,7 @@ --- title: How to Implement Feature Flags in React slug: /feature-flag-tutorials/react +pagination_next: feature-flag-tutorials/react/examples --- import VideoContent from '@site/src/components/VideoContent.jsx'; diff --git a/website/docs/feature-flag-tutorials/ruby/implementing-feature-flags-ruby.mdx b/website/docs/feature-flag-tutorials/ruby/implementing-feature-flags-ruby.mdx index 3d4a941136..1b8a30a5cd 100644 --- a/website/docs/feature-flag-tutorials/ruby/implementing-feature-flags-ruby.mdx +++ b/website/docs/feature-flag-tutorials/ruby/implementing-feature-flags-ruby.mdx @@ -35,7 +35,7 @@ For this tutorial, you’ll need the following: ![architecture diagram for our implementation](./diagram.png) -The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](https://docs.getunleash.io/reference/api/unleash) to perform the same actions straight from your CLI or server-side app. +The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](/api-overview) to perform the same actions straight from your CLI or server-side app. ## 1. Best practices for back-end apps with Unleash diff --git a/website/docs/feature-flag-tutorials/rust/implementing-feature-flags-rust.md b/website/docs/feature-flag-tutorials/rust/implementing-feature-flags-rust.md index 477c4d62ba..5761fff328 100644 --- a/website/docs/feature-flag-tutorials/rust/implementing-feature-flags-rust.md +++ b/website/docs/feature-flag-tutorials/rust/implementing-feature-flags-rust.md @@ -26,7 +26,7 @@ For this tutorial, you’ll need the following: ![architecture diagram for our implementation](./diagram.png) -The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](https://docs.getunleash.io/reference/api/unleash) to perform the same actions straight from your CLI or app. +The Unleash Server is a **Feature Flag Control Service**, which manages your feature flags and lets you retrieve flag data. Unleash has a UI for creating and managing projects and feature flags. There are also [API commands available](/api-overview) to perform the same actions straight from your CLI or app. ## 1. Install a local feature flag provider diff --git a/website/docs/feature-flag-tutorials/use-cases/a-b-testing.md b/website/docs/feature-flag-tutorials/use-cases/a-b-testing.md index 9839e4b8f5..ff29652dda 100644 --- a/website/docs/feature-flag-tutorials/use-cases/a-b-testing.md +++ b/website/docs/feature-flag-tutorials/use-cases/a-b-testing.md @@ -1,11 +1,12 @@ --- -title: How to do A/B Testing using Feature Flags +title: Implement A/B testing using feature flags slug: /feature-flag-tutorials/use-cases/a-b-testing +pagination_next: feature-flag-tutorials/use-cases/ai --- Feature flags are a great way to run A/B or multivariate tests with minimal code modifications, and Unleash offers built-in features that make it easy to get started. In this tutorial, we will walk through how to do an A/B test using Unleash with your application. -## How to Perform A/B Testing with Feature Flags +## How to perform A/B testing with feature flags To follow along with this tutorial, you need access to an Unleash instance to create and manage feature flags. Head over to our [Quick Start documentation](/quickstart) for options, including running locally or using an [Unleash SaaS instance](https://www.getunleash.io/pricing?). @@ -21,7 +22,7 @@ In this tutorial, you will learn how to set up and run an A/B test using feature You will also learn about how to [automate advanced A/B testing strategies](#multi-arm-bandit-tests-to-find-the-winning-variant) such as multi-arm bandit testing using feature flags. -### Create a Feature Flag +### Create a feature flag To do A/B testing, we'll create a feature flag to implement the rollout strategy. After that, we'll explore what strategies are and how they are configured in Unleash. @@ -39,7 +40,7 @@ Once you have completed the form, click **Create feature flag**. Your new feature flag is now ready to be used. Next, we will configure the A/B testing strategy for your flag. -### Target Users for A/B Testing +### Target users for A/B testing With an A/B testing strategy, you’ll be able to: @@ -93,13 +94,13 @@ Next, decide the percentage of users to target for each variant, known as the va ![You can configure multiple strategy variants for A/B testing within the gradual rollout form.](/img/use-case-experiment-variants.png) -### Manage User Session Behavior +### Manage user session behavior Unleash is built to give developers confidence in their ability to run A/B tests effectively. One critical component of implementing A/B testing strategies is maintaining a consistent experience for each user across multiple user sessions. For example, user `uuid1234` should be the target of `variantA` regardless of their session. The original subset of users that get `variantA` will continue to experience that variation of the feature over time. At Unleash, we call this [stickiness](/reference/stickiness). You can define the parameter of stickiness in the gradual rollout form. By default, stickiness is calculated by `sessionId` and `groupId`. -### Track A/B Testing for your Key Performance Metrics +### Track A/B testing for your key performance metrics An A/B testing strategy is most useful when you can track the results of a feature rollout to users. When your team has clearly defined the goals for your A/B tests, you can use Unleash to analyze how results tie back to key metrics, like conversion rates or time spent on a page. One way to collect this data is by enabling [impression data](/reference/impression-data) per feature flag. Impression data contains information about a specific feature flag activation check. @@ -130,7 +131,7 @@ The output from the impression data in your app may look like this code snippet: } ``` -In order to capture impression events in your app, follow our [language and framework-specific tutorials](/languages-and-frameworks). +In order to capture impression events in your app, follow our [language and framework-specific tutorials](/feature-flag-tutorials/react). Now that your application is capturing impression events, you can configure the correct data fields and formatting to send to any analytics tool or data warehouse you use. @@ -197,7 +198,7 @@ Here is an example of a payload that is returned from Google Analytics that incl By enabling impression data for your feature flag and listening to events within your application code, you can leverage this data flowing to your integrated analytics tools to make informed decisions faster and adjust your strategies based on real user behavior. -### Roll Out the Winning Variant to All Users +### Roll out the winning variant to all users After you have implemented your A/B test and measured the performance of a feature to a subset of users, you can decide which variant is the most optimal experience to roll out to all users in production. @@ -207,11 +208,11 @@ When rolling out the winning variant, your flag may already be on in your produc After the flag has been available to 100% of users over time, archive the flag and clean up your codebase. -## A/B Testing with Enterprise Automation +## A/B testing with enterprise automation With Unleash, you can automate feature flags through APIs and even rely on [actions](/reference/actions) and [signals](/reference/signals) to enable and disable flags dynamically. When running A/B tests, you can configure your projects to execute tasks in response to application metrics and thresholds you define. For example, if an experimentation feature that targets a part of your user base logs errors, your actions can automatically disable the feature so your team is given the time to triage while still providing a seamless, alternative experience to users. Similarly, you can use the APIs to modify the percentage of users targeted for variations of a feature based off users engaging with one variation more than the other. -### Multi-arm Bandit Tests to Find the Winning Variant +### Multi-arm bandit tests to find the winning variant When running complex multivariate tests with numerous combinations, automating the process of finding the best variation of a feature is the most optimal, cost-effective approach for organizations with a large user base. [Multi-arm bandit tests](https://en.wikipedia.org/wiki/Multi-armed_bandit) are a powerful technique used in A/B testing to allocate traffic to different versions of a feature or application in a way that maximizes the desired outcome, such as conversion rate or click-through rate. This approach offers several advantages over traditional A/B testing and is a viable solution for large enterprise teams. diff --git a/website/docs/feature-flag-tutorials/use-cases/ai.md b/website/docs/feature-flag-tutorials/use-cases/ai.md index 10596d0a47..dce870be87 100644 --- a/website/docs/feature-flag-tutorials/use-cases/ai.md +++ b/website/docs/feature-flag-tutorials/use-cases/ai.md @@ -1,6 +1,5 @@ --- -title: How to use feature flags with AI -slug: /feature-flag-tutorials/use-cases/ai +title: Experiment with AI using feature flags --- Hello, diff --git a/website/docs/feature-flag-tutorials/use-cases/gradual-rollout.md b/website/docs/feature-flag-tutorials/use-cases/gradual-rollout.md index 448799745f..da000000a0 100644 --- a/website/docs/feature-flag-tutorials/use-cases/gradual-rollout.md +++ b/website/docs/feature-flag-tutorials/use-cases/gradual-rollout.md @@ -1,9 +1,10 @@ --- -title: How to Perform a Gradual Rollout +title: How to perform a gradual rollout slug: /feature-flag-tutorials/use-cases/gradual-rollout +pagination_next: feature-flag-tutorials/use-cases/a-b-testing --- -## What is a Gradual Rollout? +## What is a gradual rollout? A **gradual rollout** is a controlled release strategy where a new feature is first released to a small subset of users. This allows for monitoring user behavior, identifying potential issues, and gathering feedback before a full-scale launch. It also allows us to experiment quickly and safely. @@ -13,7 +14,7 @@ Developers also use gradual rollouts to gather user feedback. Early adopters pro The key benefits of gradual rollouts are that you can experiment rapidly on a controlled group and roll back quickly if the experiment goes wrong. This reduces the risk of failure, improves software quality, improves user experience, and optimizes resource utilization. -## How to Perform a Gradual Rollout with Unleash +## How to perform a gradual rollout with Unleash To follow along with this tutorial, you will need an Unleash instance. If you’d prefer to self-host Unleash, read our [Quickstart guide](/quickstart). Alternatively, if you’d like your project to be hosted by Unleash, go to [getunleash.io](https://www.getunleash.io/pricing). @@ -41,7 +42,7 @@ Your new feature flag has been created and is ready to be used. Upon returning t Next, we will configure the gradual rollout strategy for your new flag. -## Implementing a Gradual Rollout Activation Strategy +## Implementing a gradual rollout activation strategy An important Unleash concept that enables developers to perform a gradual rollout is an [activation strategy](/reference/activation-strategies). An activation strategy defines who will be exposed to a particular flag or flags. Unleash comes pre-configured with multiple activation strategies that let you enable a feature only for a specified audience, depending on the parameters under which you would like to release a feature. @@ -75,7 +76,7 @@ Constraints and variants are not required for a gradual rollout. These additiona For gradual rollouts, _strategy constraints_ are most applicable for more granular conditions of a feature release. In the next section, we’ll explore how to apply a strategy constraint on top of a gradual rollout for more advanced use cases. -## Applying Strategy Constraints +## Applying strategy constraints When utilizing an activation strategy such as a gradual rollout, it may be necessary to further define which subset of users get access to a feature and when the rollout takes place, depending on the complexity of your use case. Unleash provides [strategy constraints](/reference/activation-strategies#constraints) as a way to fine-tune conditions under which a feature flag is evaluated. @@ -94,11 +95,11 @@ Within a gradual rollout activation strategy, you can use strategy constraints t Add [constraints](/reference/activation-strategies#constraints) to refine the rollout based on user attributes, segments, or conditions. -### Define Custom Context Fields for Strategy Constraints +### Define custom context fields for strategy constraints If you want to create new types of constraints that are not built into Unleash, you can create [custom context fields](/reference/unleash-context#custom-context-fields) to use in your gradual rollout for more advanced use cases. -## Leveraging Segments +## Using segments A [segment](/reference/segments) is a reusable collection of [strategy constraints](/reference/activation-strategies#constraints). Like with strategy constraints, you apply segments to feature flag activation strategies. @@ -125,7 +126,7 @@ You must pass the relevant fields in your context in the SDK in order for gradua By following these steps and leveraging Unleash's features, you can effectively execute and refine gradual rollouts to minimize risks and optimize feature delivery. -## Managing Gradual Rollouts With Enterprise Security In Mind +## Managing gradual rollouts with enterprise security in mind For large-scale organizations, managing feature flags across many teams can be complex and challenging. Unleash was architected for your feature flag management to be scalable and traceable for enterprises, which boosts overall internal security posture while delivering software efficiently. @@ -137,7 +138,7 @@ After you have implemented a gradual rollout strategy, we recommend managing the Read our documentation on how to effectively manage [feature flags at scale](/topics/feature-flags/best-practices-using-feature-flags-at-scale) while reducing security risks. Let’s walk through these recommended Unleash features in the subsequent sections. -### Reviewing Application Metrics +### Reviewing application metrics [Unleash metrics](/reference/api/unleash/metrics) are a great way to understand user traffic. With your application using feature flags, you can review: @@ -149,7 +150,7 @@ Read our documentation on how to effectively manage [feature flags at scale](/to When managing a gradual rollout, leverage metrics to gain deeper insight into flag usage against your application over time. For large-scale organizations with many feature flags to manage, this can be a useful monitoring tool for individual flags you would like to keep track of. -### Reviewing Audit Logs +### Reviewing audit logs Because a feature flag service controls the way an application behaves in production, it can be highly important to have visibility into when changes have been made and by whom. This is especially true in highly regulated environments. In these cases, you might find audit logging useful for: @@ -164,7 +165,7 @@ Unleash provides the data to log any change that has happened over time, at the Learn more about [Event Log](/reference/events#event-log) in our documentation. -### Managing Change Requests +### Managing change requests You can use Unleash's [change request](/reference/change-requests) feature to propose and review modifications to feature flags. This gives developers complete control over your production environment. In large scale organizations and heavily regulated industries, we want to help developers reduce risk of errors in production or making unwanted changes by team members that have not been properly reviewed and approved. diff --git a/website/docs/how-to/how-to-capture-impression-data.mdx b/website/docs/feature-flag-tutorials/use-cases/how-to-capture-impression-data.mdx similarity index 76% rename from website/docs/how-to/how-to-capture-impression-data.mdx rename to website/docs/feature-flag-tutorials/use-cases/how-to-capture-impression-data.mdx index 0341844fc8..f7039814a8 100644 --- a/website/docs/how-to/how-to-capture-impression-data.mdx +++ b/website/docs/feature-flag-tutorials/use-cases/how-to-capture-impression-data.mdx @@ -1,15 +1,12 @@ --- -title: How to capture impression data +title: Send impression data to analytics tools --- import ApiRequest from "@site/src/components/ApiRequest"; import VideoContent from "@site/src/components/VideoContent.jsx"; -:::info Placeholders -Placeholders in the code samples below are delimited by angle brackets (i.e. ``). You will need to replace them with the values that are correct in _your_ situation for the code samples to run properly. -::: -Unleash allows you to gather [**impression data**](../reference/impression-data) from your feature flags, giving you complete visibility into who checked what flags and when. What you do with this data is entirely up to you, but a common use case is to send it off to an aggregation and analytics service such as [Posthog](https://posthog.com/) or [Google Analytics](https://analytics.google.com/), either just for monitoring purposes or to facilitate [A/B testing](../feature-flag-tutorials/use-cases/a-b-testing). +Unleash allows you to gather [impression data](/reference/impression-data) from your feature flags, giving you complete visibility into who checked what flags and when. What you do with this data is entirely up to you, but a common use case is to send it off to an aggregation and analytics service such as [Posthog](https://posthog.com/) or [Google Analytics](https://analytics.google.com/), either just for monitoring purposes or to facilitate [A/B testing](/feature-flag-tutorials/use-cases/a-b-testing). This guide will take you through everything you need to do in Unleash to facilitate such a workflow. It will show you how to send data to Posthog as an example sink, but the exact same principles will apply to any other service of the same kind. @@ -22,15 +19,15 @@ We will assume that you have the necessary details for your third-party service: - **where to send the data to**. We'll refer to this in the code samples below as **``**. - **what format the data needs to be in**. This will determine how you transform the event data before you send it. -In addition, you'll need to have a flag to record impression data for and an [Unleash client SDK](../reference/sdks) that supports impression data. This guide will use the [JavaScript proxy SDK](/reference/sdks/javascript-browser). +In addition, you'll need to have a flag to record impression data for and an [Unleash client SDK](/reference/sdks) that supports impression data. This guide will use the [JavaScript proxy SDK](/reference/sdks/javascript-browser). When you have those things sorted, follow the below steps. -## Step 1: Enable impression data for your feature flag {#step-1} +## Enable impression data for your feature flag Because impression data is an **opt-in feature**, the first step is to enable it for the feature you want to gather data from. You can use both the UI and the API. -### Enabling impression data for new feature flags {#step-1-new-toggles} +### Enable impression data for new feature flags When you create a new feature flag via the UI, there's an option at the end of the form that you must enable: @@ -45,7 +42,7 @@ To create a feature flag with impression data enabled, set the `impressionData` title="Create a feature flag with impression data enabled." /> -### Enabling impression data for existing feature flags {#step-1-existing-toggles} +### Enable impression data for existing feature flags To enable impression data for an existing flag, go to the "Settings" tab of that feature flag and use the "edit" button near the Feature information title in the admin UI. It will take you to a form that looks like the flag creation form. Use the "Enable impression data" flag to enable it, the same way you would when [enabling impression data for a new feature flag](#step-1-new-toggles). @@ -60,9 +57,9 @@ To enable impression data for an existing flag, use the [API's flag patching fun title="Enable impression data on an existing flag." /> -## Step 2: Capture impression events in your client {#step-2} +## Capture impression events in your client -In your client SDK, initialize the library for the third party service you're using. +In your client SDK, initialize the library for the third-party service you're using. For the full details on setting up a Posthog client, see [the official Posthog JavaScript client docs](https://posthog.com/docs/integrate/client/js). The steps below will use extracts from said documentation. diff --git a/website/docs/feature-flag-tutorials/use-cases/how-to-create-feature-toggles.md b/website/docs/feature-flag-tutorials/use-cases/how-to-create-feature-toggles.md new file mode 100644 index 0000000000..7afa5dcfdb --- /dev/null +++ b/website/docs/feature-flag-tutorials/use-cases/how-to-create-feature-toggles.md @@ -0,0 +1,52 @@ +--- +title: Create and configure a feature flag +description: 'This guide shows you how to create feature flags in Unleash and how to add constraints, segments, variants, and more.' +slug: /how-to-create-feature-flag +pagination_next: how-to/how-to-schedule-feature-releases +--- + +[Feature flags](../reference/feature-toggles) are a foundational component of Unleash, enabling you to manage features dynamically. This guide details the process of creating and configuring feature flags within Unleash. You'll learn how to create flags, define activation strategies, enable them, and optionally refine their behavior with constraints, segments, and variants. + +This guide focuses on the Unleash Admin UI, but you can also use the [Admin API](/reference/api/unleash/create-feature) to create, update, and manage feature flags. + +## Create a feature flag + +To create a feature flag in the Admin UI, do the following: + +1. Go to a project and click **Create feature flag**. +2. Enter a unique name for the flag and click **Create feature flag**. + +## Add an activation strategy + +[Activation strategies](/reference/activation-strategies) determine how and for whom a feature flag is enabled within a specific environment (for example, development, or production). To add an activation strategy to your feature flag in the Admin UI, do the following: + +1. Go to a feature flag, and select the [environment](/reference/environments) where you want to configure the flag. +2. Click **Add strategy** for that environment. +3. In the **General** tab, select your rollout percentage. Optionally, you can set the strategy status to **Inactive** if you don't yet want the strategy to be exposed. +4. Click **Save strategy**. + +## Enable the feature flag in an environment + +To enable the feature flag in an environment, use the main environment toggle to enable the flag in the environment. This ensures that the feature flag is evaluated using the rules of its activation strategies and the provided [Unleash context](/reference/unleash-context). + +## Refine targeting with constraints and segments + +[Strategy constraints](/reference/activation-strategies#constraints) and [segments](../reference/segments) allow you to apply fine-grained filters to your activation strategies, ensuring they only activate for users and applications matching specific criteria. You can add constraints or segments when creating or editing an existing activation strategy. + +1. Go to the feature flag and the environment containing the strategy you want to modify and click the **Edit strategy**. +2. In the Targeting Tab, click **Add constraint**. +3. To define the constraint, add a context field, an operator, and values to compare against. +4. Add more constraints if needed, and click **Save strategy**. + +[Segments](/reference/segments) work similarly to constraints, but they are a reusable set of constraints that you define once and can reuse across flags. + +### Configure strategy variants + +[Variants](../reference/feature-toggle-variants) give you the ability to further target your users and split them into groups of your choosing, such as for A/B testing. + +1. Go to the feature flag and the environment containing the strategy you want to modify and click the **Edit strategy**. +2. In the **Variants** tab, click **Add variant**. +3. Enter the variant name and an optional payload. +4. Optionally, click **Add variant** again to add more variants. +5. Toggle **Custom percentage** for fixed weights, if required. +6. Click **Save strategy**. \ No newline at end of file diff --git a/website/docs/feature-flag-tutorials/use-cases/scaling-unleash.mdx b/website/docs/feature-flag-tutorials/use-cases/scaling-unleash.mdx index 62028dc694..4a4b31c05b 100644 --- a/website/docs/feature-flag-tutorials/use-cases/scaling-unleash.mdx +++ b/website/docs/feature-flag-tutorials/use-cases/scaling-unleash.mdx @@ -1,5 +1,5 @@ --- -title: Scaling Unleash for Enterprise Workloads +title: Scaling Unleash for enterprise workloads --- import Tabs from '@theme/Tabs'; diff --git a/website/docs/feature-flag-tutorials/use-cases/security-compliance.md b/website/docs/feature-flag-tutorials/use-cases/security-compliance.md index 59263543ff..3aab9ad9b4 100644 --- a/website/docs/feature-flag-tutorials/use-cases/security-compliance.md +++ b/website/docs/feature-flag-tutorials/use-cases/security-compliance.md @@ -1,5 +1,5 @@ --- -title: Feature Flag Security and Compliance for Enterprises +title: Feature flag security and compliance for enterprises slug: /feature-flag-tutorials/use-cases/security-and-compliance --- @@ -42,7 +42,7 @@ Your developers and other stakeholders need to securely access platforms used to To use single sign-on in Unleash, your users can authenticate themselves through OpenID Connect (OIDC) or SAML 2.0 protocols. -We have integration guides to connect Unleash to enterprise identity providers like Okta, Microsoft Entra ID, and Keycloak, but you can use any identity provider that uses OIDC or SAML 2.0 protocol. Read our [how-to guide for single sign-on](/how-to/sso). +We have integration guides to connect Unleash to enterprise identity providers like Okta, Microsoft Entra ID, and Keycloak, but you can use any identity provider that uses OIDC or SAML 2.0 protocol. Read our [how-to guide for single sign-on](/how-to/how-to-add-sso-open-id-connect). ![A diagram showing how Unleash integrates with authentication providers and identity providers.](/img/sso-idp-auth-provider.jpg) @@ -65,7 +65,7 @@ By enabling [SCIM](/reference/scim) in Unleash, you can: - Sync group membership. - Ensure consistent access across multiple platforms. -To unlock these benefits, set up [SCIM for automatic provisioning using our how-to guides](/how-to/provisioning). +To unlock these benefits, set up [SCIM for automatic provisioning using our how-to guides](/how-to/how-to-setup-provisioning-with-okta). ## Configure role-based access control for administrators and developers @@ -88,7 +88,7 @@ Unleash is built with many mechanisms in place to handle all of these scenarios. Let’s look at how Unleash gives you complete control over user roles and permissions. At a high level, there are multiple [predefined roles](/reference/rbac#predefined-roles) in Unleash for you to get started with. Root roles control permissions to top-level resources, spanning across all projects. Project roles, on the other hand, control permissions for a project, the feature flags, and individual configurations per environment. -The three predefined root roles are: Admin, Editor, and Viewer. The predefined project roles are Owner and Member. In addition to these, you can also create [custom root](/how-to/how-to-create-and-assign-custom-root-roles) or [project roles](/how-to/how-to-create-and-assign-custom-project-roles). The following diagram provides a visual overview of how root roles and project roles compare. +The three predefined root roles are: Admin, Editor, and Viewer. The predefined project roles are Owner and Member. In addition to these, you can also create [custom root roles](/reference/rbac#create-and-assign-a-custom-root-role) or [project roles](/reference/rbac#create-and-assign-a-custom-project-role). The following diagram provides a visual overview of how root roles and project roles compare. ![The diagram showing the relationship between root roles and project roles in Unleash.](/img/root-and-project-roles-comparison.jpg) @@ -171,7 +171,7 @@ For more advanced implementations, integrate Unleash event logs directly into br ### Leverage access logs for broader auditing -Let’s think back to the importance of user management that we covered earlier. Developers and other stakeholders go through onboarding to use the platform. Authentication protocols and user provisioning ensure these processes are secure, unified, and automated. During this process, access logs keep track of what users and systems accessed Unleash and what actions they performed, including [Unleash API interactions](/reference/api/unleash) from your services and applications. You can export these logs to S3 buckets for long-term data storage. This is valuable if you need to preserve data for complying with legal or regulatory compliance, storing critical backups for disaster recovery, and archiving. +Let’s think back to the importance of user management that we covered earlier. Developers and other stakeholders go through onboarding to use the platform. Authentication protocols and user provisioning ensure these processes are secure, unified, and automated. During this process, access logs keep track of what users and systems accessed Unleash and what actions they performed, including [Unleash API interactions](/api-overview) from your services and applications. You can export these logs to S3 buckets for long-term data storage. This is valuable if you need to preserve data for complying with legal or regulatory compliance, storing critical backups for disaster recovery, and archiving. Auditing your feature flag system is made simple for traceability and reportability with Unleash’s event logs and access logs. We recommend leveraging these features as data sources for third-party services that make your data a valuable asset for security reviews, meeting compliance standards, and overall risk mitigation. diff --git a/website/docs/feature-flag-tutorials/use-cases/trunk-based-development.md b/website/docs/feature-flag-tutorials/use-cases/trunk-based-development.md index 95081dccba..cc5fd5ab7d 100644 --- a/website/docs/feature-flag-tutorials/use-cases/trunk-based-development.md +++ b/website/docs/feature-flag-tutorials/use-cases/trunk-based-development.md @@ -1,5 +1,5 @@ --- -title: How to do Trunk-Based Development using Feature Flags +title: Implement trunk-based development using feature flags slug: /feature-flag-tutorials/use-cases/trunk-based-development --- diff --git a/website/docs/feature-flag-tutorials/use-cases/user-management-access-controls.md b/website/docs/feature-flag-tutorials/use-cases/user-management-access-controls.md index 8654768066..5751de4daa 100644 --- a/website/docs/feature-flag-tutorials/use-cases/user-management-access-controls.md +++ b/website/docs/feature-flag-tutorials/use-cases/user-management-access-controls.md @@ -1,6 +1,7 @@ --- -title: How to Implement User Management, Access Controls, and Auditing with Feature Flags +title: Implement user management, access controls, and auditing with feature flags slug: /feature-flag-tutorials/use-cases/user-management-access-controls-auditing +pagination_next: feature-flag-tutorials/use-cases/security-compliance --- Feature flags are a game-changer for how software teams build, test, and release products. They enable you to roll out new features with confidence, manage risk, and keep your software development agile and secure. @@ -19,7 +20,7 @@ In this tutorial, you will: When an enterprise like a global banking platform considers implementing feature flags, keeping track of who can access your feature flag platform and handling authentication is critical. Traditional username and password approaches are insecure and [shared accounts pose a security risk](https://www.getunleash.io/blog/stop-sharing-accounts). -To ensure proper user authentication and reduce risk exposure, Unleash provides [single sign-on](/how-to/sso) as the recommended centralized method for managing user access. +To ensure proper user authentication and reduce risk exposure, Unleash provides [single sign-on](/how-to/how-to-add-sso-open-id-connect) as the recommended centralized method for managing user access. Unleash supports any SSO option through OpenID Connect or SAML 2.0, including identity providers like Okta, Microsoft Entra ID, and Keycloak to create a unified authentication process. @@ -97,7 +98,7 @@ Viewers can observe projects and flags, but cannot make changes. When added to a Project permissions are separated from root permissions to make it even more targeted regarding what permissions someone can and cannot have in Unleash. -For more fine-tuned access controls, create [custom root roles](/how-to/how-to-create-and-assign-custom-root-roles) and [custom project roles](/how-to/how-to-create-and-assign-custom-project-roles), where you can define the privileges and limitations beyond the predefined roles we have built into Unleash. +For more fine-tuned access controls, create [custom root roles](/reference/rbac#create-and-assign-a-custom-root-role) and [custom project roles](/reference/rbac#create-and-assign-a-custom-project-role), where you can define the privileges and limitations beyond the predefined roles we have built into Unleash. For example, customize root permissions to perform CRUD operations for: diff --git a/website/docs/how-to/how-to-add-sso-azure-saml.mdx b/website/docs/how-to/how-to-add-sso-azure-saml.mdx index 95e2747fda..b237c33b0d 100644 --- a/website/docs/how-to/how-to-add-sso-azure-saml.mdx +++ b/website/docs/how-to/how-to-add-sso-azure-saml.mdx @@ -1,5 +1,5 @@ --- -title: How to add SSO with SAML 2.0 and Microsoft Entra ID +title: Set up SSO with SAML 2.0 and Microsoft Entra ID description: 'Configure Microsoft Entra ID SSO with SAML 2.0 for your Unleash instance.' --- diff --git a/website/docs/how-to/how-to-add-sso-google.md b/website/docs/how-to/how-to-add-sso-google.md index 7a495a920d..1e81959595 100644 --- a/website/docs/how-to/how-to-add-sso-google.md +++ b/website/docs/how-to/how-to-add-sso-google.md @@ -1,5 +1,6 @@ --- -title: 'How to add SSO with Google' +title: 'Set up SSO with Google' +description: Set up SSO for Unleash with Google. --- :::caution Deprecation notice diff --git a/website/docs/how-to/how-to-add-sso-open-id-connect.md b/website/docs/how-to/how-to-add-sso-open-id-connect.md index 9896149571..4b41e568c7 100644 --- a/website/docs/how-to/how-to-add-sso-open-id-connect.md +++ b/website/docs/how-to/how-to-add-sso-open-id-connect.md @@ -1,5 +1,7 @@ --- -title: How to add SSO with OpenID Connect +title: Set up SSO with OpenID Connect +description: Set up SSO for Unleash with OpenID Connect. +pagination_next: how-to/how-to-add-sso-saml --- :::note Availability diff --git a/website/docs/how-to/how-to-add-sso-saml-keycloak.md b/website/docs/how-to/how-to-add-sso-saml-keycloak.md index ff7092a67a..9abc52e04f 100644 --- a/website/docs/how-to/how-to-add-sso-saml-keycloak.md +++ b/website/docs/how-to/how-to-add-sso-saml-keycloak.md @@ -1,5 +1,6 @@ --- -title: How to add SSO with SAML 2.0 Keycloak +title: Set up SSO with SAML 2.0 and Keycloak +description: Set up SSO for Unleash with SAML 2.0 and Keycloak. --- :::note Availability diff --git a/website/docs/how-to/how-to-add-sso-saml.md b/website/docs/how-to/how-to-add-sso-saml.md index 64fb1ddd44..5e148e316d 100644 --- a/website/docs/how-to/how-to-add-sso-saml.md +++ b/website/docs/how-to/how-to-add-sso-saml.md @@ -1,5 +1,6 @@ --- -title: How to add SSO with SAML 2.0 Okta +title: Set up SSO with SAML 2.0 and Okta +description: Set up SSO for Unleash with SAML 2.0 and Okta. --- :::note Availability diff --git a/website/docs/how-to/how-to-add-users-to-unleash.md b/website/docs/how-to/how-to-add-users-to-unleash.md deleted file mode 100644 index ef1472724e..0000000000 --- a/website/docs/how-to/how-to-add-users-to-unleash.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: How to add new users to your Unleash instance ---- - -:::note Availability - -**Version**: `4.0+` - -::: - - -You can add new users to Unleash in `Admin > Users`. - -1. From the top-line menu – click on the “Settings Wheel” then click on “Users”. - ![A visual representation of the current step: the Unleash Admin UI with the steps highlighted.](/img/user_admin_list_button.png) - - -2. To add a new user to your Unleash instance, use the "new user" button: - ![The Unleash users page with the 'add new user' button being pointed to.](/img/user_admin-add-user.jpg) - -3. Fill out the required fields in the "create user" form. Refer to the [predefined roles overview](../reference/rbac.md#predefined-roles) for more information on roles. - - ![A form titled "Add team member". It has the fields "full name", "email", and "role". The role field is a radio button set with roles called "admin", "editor", and "viewer".](/img/user_admin_add_user_modal.png) - - If you have configured an email server the user will receive the invite link in her inbox, otherwise you should share the magic invite link to Unleash presented in the confirmation dialogue. diff --git a/website/docs/how-to/how-to-create-and-assign-custom-project-roles.mdx b/website/docs/how-to/how-to-create-and-assign-custom-project-roles.mdx deleted file mode 100644 index d8d8f3e1e9..0000000000 --- a/website/docs/how-to/how-to-create-and-assign-custom-project-roles.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: How to create and assign custom project roles ---- - -import VideoContent from '@site/src/components/VideoContent.jsx' - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `4.6+` - -::: - - -This guide takes you through [how to create](#creating-custom-project-roles "how to create custom project roles") and [assign](#assigning-custom-project-roles "how to assign custom project roles") [custom project roles](../reference/rbac.md#custom-project-roles). Custom project roles allow you to fine-tune access rights and permissions within your projects. - - - -## Creating custom project roles - -It takes about three steps to create custom project roles: - -1. Navigate to the custom project roles page by using the admin menu (the gear symbol) and navigating to users. - ![A visual representation of the current step: the Unleash Admin UI with the steps highlighted.](/img/create-cpr-step-1.png) -2. Navigate to the "Project roles" tab. - ![The admin/roles screen, with the project roles tab highlighted. The page shows a table of project roles with their descriptions.](/img/create-cpr-step-2.png) -3. Use the "New project role" button to open the role creation form. - ![The visual position of the 'new project role' button on the page.](/img/create-cpr-step-3.png) -4. Give the role a name, an optional description, and the set of permissions you'd like it to have. For a full overview of all the options, consult the [custom project roles reference documentation](../reference/rbac.md#custom-project-roles). - ![The project role creation form filled in with details for a "developer" role. To the left is the equivalent cURL command you could run if you wanted to use the API instead of the form.](/img/create-cpr-step-4.png) - - - -## Assigning custom project roles - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `5.6+`. - -::: - -Assigning a custom project role is a pretty straightforward process and requires three steps, outlined below. - -To assign a custom project role to a user: -1. Navigate to the project you want to assign the user a role in. - ![The steps to navigate to a project: use the 'projects' navigation item and select your project.](/img/assign-cpr-step-1.png) -2. Navigate to the project's _access_ tab. - ![A project overview with the 'access' tab highlighted.](/img/assign-cpr-step-2.png) -3. This step depends on whether the user has already been added to the project or not: - - If the user has already been added to the project, click on the edit icon corresponding with its line and from the overlay that will show up select the new role you want to assign it from the dropdown and save the changes. - ![A list of users with access to the current project. To the right of each user is a dropdown input labeled role.](/img/assign-cpr-step-3a.png) - - If the user _hasn't_ been added to the project, add them using the button 'Assign user/group'. From the overlay that will show up select the user, assign it a role and save the changes. Now you should be able to see the new user in the table. - ![Adding a user to a project. The add user form is filled out with data for an "Alexis". The Role input is open and the custom "Developer" role is highlighted.](/img/assign-cpr-step-3b.png) diff --git a/website/docs/how-to/how-to-create-and-assign-custom-root-roles.md b/website/docs/how-to/how-to-create-and-assign-custom-root-roles.md deleted file mode 100644 index 8282eec285..0000000000 --- a/website/docs/how-to/how-to-create-and-assign-custom-root-roles.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: How to create and assign custom root roles ---- - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `5.4+` - -::: - - -This guide takes you through [how to create](#creating-custom-root-roles "how to create custom root roles") and [assign](#assigning-custom-root-roles "how to assign custom root roles") [custom root roles](../reference/rbac.md#custom-root-roles). Custom root roles allow you to fine-tune access rights and permissions to root resources in your Unleash instance. - -## Creating custom root roles - -### Step 1: Navigate to the custom root roles page {#create-step-1} - -Navigate to the _roles_ page in the admin UI (available at the URL `/admin/roles`). Use the _settings_ button in the navigation menu and select "roles". - -![The admin UI admin menu with the Roles item highlighted.](/img/create-crr-step-1.png) - -### Step 2: Click the "new root role" button. {#create-step-2} - -Use the "new root role" button to open the "new root role" form. - -![The "root roles" table with the "new root role" button highlighted.](/img/create-crr-step-2.png) - -### Step 3: Fill in the root role form {#create-step-3} - -Give the root role a name, a description, and the set of permissions you'd like it to have. For a full overview of all the options, consult the [custom root roles reference documentation](../reference/rbac.md#custom-root-roles). - -![The root role form filled with some example data, and the "add role" button highlighted at the bottom.](/img/create-crr-step-3.png) - -## Assigning custom root roles - -You can assign custom root roles just like you would assign any other [predefined root role](../reference/rbac.md#predefined-roles). Root roles can be assigned to users, [service accounts](../reference/service-accounts.md), and [groups](../reference/rbac.md#user-groups). diff --git a/website/docs/how-to/how-to-create-and-display-banners.md b/website/docs/how-to/how-to-create-and-display-banners.md deleted file mode 100644 index a1fe5614e2..0000000000 --- a/website/docs/how-to/how-to-create-and-display-banners.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: How to create and display banners ---- - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `5.7+` - -::: - - -This guide takes you through [how to create](#creating-banners "how to create banners") and [display](#displaying-banners "how to display banners") [banners](../reference/banners.md). - -## Creating banners - -### Step 1: Navigate to the banners page {#create-step-1} - -Navigate to the _banners_ page in the admin UI (available at the URL `/admin/banners`). Use the _settings_ button in the navigation menu and select "banners". - -![The admin UI admin menu with the Banners item highlighted.](/img/create-banners-step-1.png) - -### Step 2: Use the "new banner" button {#create-step-2} - -Use the "new banner" button to open the "new banner" form. - -![The "banners" table with the "new banner" button highlighted.](/img/create-banners-step-2.png) - -### Step 3: Fill in the banner form {#create-step-3} - -Choose whether the banner should be enabled right away. If enabled, the banner will be visible to all users in your Unleash instance. Select the banner type, icon, and write the message that you'd like to see displayed on the banner. The message and dialog fields support [Markdown](https://www.markdownguide.org/basic-syntax/). Optionally, you can also configure a banner action for user interactivity. For a full overview of all the banner options, consult the [banners reference documentation](../reference/banners). - -You'll be able to preview the banner at the top as you fill in the form. - -Once you're satisfied, use the "add banner" button to create the banner. - -![The banner form filled with some example data, and the "add banner" button highlighted at the bottom.](/img/create-banners-step-3.png) - -## Displaying banners - -You can choose whether a banner is currently displayed to all users of your Unleash instance by toggling the "enabled" switch on the banner table. - -Alternatively, you can edit the banner by using the "edit" button on the banner table and then toggle the "banner status" switch. - -![The "banners" table with some example data, and the "enable" switch highlighted.](/img/create-banners-display.png) diff --git a/website/docs/how-to/how-to-create-and-manage-user-groups.md b/website/docs/how-to/how-to-create-and-manage-user-groups.md deleted file mode 100644 index 1a1f22c195..0000000000 --- a/website/docs/how-to/how-to-create-and-manage-user-groups.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: How to create and manage user groups ---- - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `4.14+` - -::: - -This guide takes you through how to use user groups to manage permissions on your projects. User groups allow you to manage large groups of users more easily than assigning roles directly to those users. Refer to the section on [user groups](../reference/rbac.md#user-groups) in the RBAC documentation for more information. - -## Creating user groups - -1. Navigate to groups by using the admin menu (the gear icon) and selecting the groups option. - -![The Unleash Admin UI with the steps highlighted to navigate to groups.](/img/create-ug-step-1.png) - -2. Navigate to new group. - -![The groups screen with the new group button highlighted.](/img/create-ug-step-2.png) - -3. Give the group a name, an optional description, an optional root role, and select the users you'd like to be in the group. - -![The new group screen with the users drop down open and highlighted.](/img/create-ug-step-3.png) - -4. Review the details of the group and save them if you're happy. - -![The new group screen with the users selected and the save button highlighted.](/img/create-ug-step-4.png) - -## Managing users within a group - -1. Navigate to groups by using the admin menu (the gear icon) and selecting the groups option. - -![The Unleash Admin UI with the steps highlighted to navigate to groups.](/img/create-ug-step-1.png) - -2. Select the card of the group you want to edit. - -![The manage groups with a pointer to a group card.](/img/edit-ug-step-2.png) - -3. Remove users by using the remove user button (displayed as a bin). - -![The manage group page with the remove user button highlighted.](/img/remove-user-from-group-step-1.png) - -4. Confirm the remove. - -![The manage group page with the confirm user removal dialog shown.](/img/remove-user-from-group-step-2.png) - -5. Add users by selecting the add button. - -![The groups page shown with the add user button highlighted.](/img/add-user-to-group-step-1.png) - -6. Find the user you'd like to add to the group and select them. - -![The groups page shown with a user selected.](/img/add-user-to-group-step-2.png) - -7. Review the group users and save when you're happy. - -![The edit groups page shown with the save button highlighted.](/img/add-user-to-group-step-3.png) - -## Assigning groups to projects - -1. Navigate to projects - -![The landing page with the projects navigation link highlighted.](/img/add-group-to-project-step-1.png) - -2. Select the project you want to manage. - -![The projects page with a project highlighted.](/img/add-group-to-project-step-2.png) - -3. Navigate to the access tab and then use the assign user/group button. - -![The project page with the access tab and assign button highlighted.](/img/add-group-to-project-step-3.png) - -4. Find your group in the drop down. - -![The access sidepane for a project with a group selected.](/img/add-group-to-project-step-4.png) - -5. Select the role that the group should have in this project. You can review the list of permissions that the group users will gain by having this role before confirming. - -![The access sidepane for a project with a role selected.](/img/add-group-to-project-step-5.png) diff --git a/website/docs/how-to/how-to-create-api-tokens.mdx b/website/docs/how-to/how-to-create-api-tokens.mdx index 95b73bbc43..0ff6ed310b 100644 --- a/website/docs/how-to/how-to-create-api-tokens.mdx +++ b/website/docs/how-to/how-to-create-api-tokens.mdx @@ -2,9 +2,21 @@ title: How to create API Tokens --- -Depending on your [permissions](../reference/api-tokens-and-client-keys#api-token-permissions), you can create API tokens in the Unleash Admin UI in three ways: +All Unleash APIs require authentication using an [API token](/reference/api-tokens-and-client-keys). The type of token you use depends on the API you are accessing and your specific use case. + +### Token types + +Unleash supports four types of API tokens: +- **Client tokens**: Used to connect server-side SDKs, Unleash Edge, and the Unleash Proxy to the Client API. Can be scoped to a specific project and environment. +- **Frontend tokens**: Used to connect client-side SDKs to the Frontend API or Unleash Edge. These tokens are designed to be publicly accessible and have limited permissions. Can be scoped to a specific project and environment. +- **Personal access tokens**: Tied to a specific user account. They are useful for testing, debugging, or providing temporary access to tools and scripts that need to interact with the Admin API. +- **Service account tokens**: The recommended method for providing API access to integrations, automation tools, and other non-human users. Service accounts provide a more secure and manageable way to grant Admin API access. + +### Create an API token + +Depending on your permissions, you can create API tokens in the Unleash Admin UI in four ways: + - **Admin settings > Access control > API access**: for client or frontend tokens; requires the Admin root role, or a custom root role with API token permissions. -- **Settings > API access** inside a project: for project-specific client or frontend tokens; permitted for project Members or users with a [corresponding root role](../reference/api-tokens-and-client-keys#api-token-permissions). +- **Admin settings > Service accounts > New service account**: for creating a service account and assigning a token. +- **Settings > API access** [inside a project]: for project-specific client or frontend tokens; permitted for project Members or users with a corresponding root role. - **Profile > View profile settings > Personal API tokens**: for personal access tokens. - - diff --git a/website/docs/how-to/how-to-create-feature-toggles.md b/website/docs/how-to/how-to-create-feature-toggles.md deleted file mode 100644 index 62d34d5a3f..0000000000 --- a/website/docs/how-to/how-to-create-feature-toggles.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: How to create a feature flag -description: 'This guide shows you how to create feature flags in Unleash and how to add constraints, segments, variants, and more.' -slug: /how-to-create-feature-flag ---- - -[Feature flags](../reference/feature-toggles) are the foundation of Unleash. They are at the core of everything we do and are a fundamental building block in any feature management system. This guide shows you how to create feature flags in Unleash and how to add any optional constraints, segments, variants, and more. Links to learn more about these concepts will be scattered throughout the text. - -You can perform every action both via the UI and the admin API. This guide includes screenshots to highlight the relevant UI controls and links to the relevant API methods for each step. - -This guide is split into three sections: - -1. [Prerequisites](#prerequisites): you need these before you can create a flag. -2. [Required steps](#required-steps): all the required steps to create a flag and activate it in production. -3. [Optional steps](#optional-steps): optional steps you can take to further target and configure your feature flag and its audience. - -## Prerequisites - -To perform all the steps in this guide, you will need: - -- A running Unleash instance -- A project to hold the flag -- A user with an **editor** or **admin** role OR a user with the following permissions inside the target project: - - **[project-level permissions](../reference/rbac#project-permissions)** - - create feature flags - - **[environment-level permissions](../reference/rbac#environment-permissions)** - - create/edit variants[^1] - - create activation strategies - - update activation strategies - - enable/disable flags - -:::info roles - -Refer to [the documentation on role-based access control](../reference/rbac) for more information about the available roles and their permissions. - -::: - -## Required steps - -This section takes you through the required steps to create and activate a feature flag. It assumes that you have all the prerequisites from the previous section done. - -### Step 1: Create a flag {#step-1} - -:::tip API: create a flag - -Use the [Admin API endpoint for creating a feature flag](/reference/api/legacy/unleash/admin/features-v2#create-toggle). The payload accepts all the same fields as the Admin UI form. The Admin UI also displays the corresponding cURL command when you use the form. - -::: - -In the project that you want to create the flag in, use the "new feature flag" button and fill the form out with your desired configuration. Refer to the [feature flag reference documentation](../reference/feature-toggles) for the full list of configuration options and explanations. - -![](/img/create-toggle-new-toggle.png) - -### Step 2: Add a strategy {#step-2} - -:::tip API: Add a strategy - -Use the [API for adding a strategy to a feature flag](/reference/api/legacy/unleash/admin/features-v2#add-strategy). You can find the configuration options for each strategy in the [activation strategy reference documentation](../reference/activation-strategies). - -::: - -Decide which environment you want to enable the flag in. Select that environment and add an activation strategy. Different activation strategies will act differently as described in the [activation strategy documentation](../reference/activation-strategies). The configuration for each strategy differs accordingly. After selecting a strategy, complete the steps to configure it. - -![](/img/create-toggle-add-strategy.png) - -### Step 3: Enable the flag {#step-3} - -:::tip API: Enable a flag - -Use the [API for enabling an environment for a flag](/reference/api/legacy/unleash/admin/features-v2#enable-env) and specify the environment you'd like to enable. - -::: - -Use the environments flags to switch on the environment that you chose above. Depending on the activation strategy you added in the previous step, the flag should now evaluate to true or false depending on the Unleash context you provide it. - -![](/img/create-toggle-enable-env.png) - -## Optional steps - -These optional steps allow you to further configure your feature flags to add optional payloads, variants for A/B testing, more detailed user targeting and exceptions/overrides. - -### Add constraints and segmentation - -Constraints and segmentation allow you to set filters on your strategies, so that they will only be evaluated for users and applications that match the specified preconditions. Refer to the [strategy constraints](../reference/activation-strategies#constraints) and [segments reference documentation](../reference/segments) for more information. - -To add constraints and segmentation, use the "edit strategy" button for the desired strategy. - -![](/img/create-toggle-edit-strategy.png) - -#### Constraints - -:::info - -Constraints aren't fixed and can be changed later to further narrow your audience. You can add them either when you add a strategy to a flag or at any point thereafter. - -::: - -:::tip API: Add constraints - -You can either [add constraints when you add the strategy](/reference/api/unleash/add-feature-strategy) or [PUT](/reference/api/unleash/update-feature-strategy) or [PATCH](/reference/api/unleash/patch-feature-strategy) the strategy later. - -::: - -In the strategy configuration screen for the strategy that you want to configure, use the "add constraint" button to add a strategy constraint. - -![](/img/create-toggle-add-constraint.png) - -#### Segments - -:::info - -This can be done after you have created a strategy. - -::: - -:::tip API: add segments - -Use the [API for adding segments to a strategy](/reference/api/unleash/update-feature-strategy-segments) to add segments to your strategy. - -::: - -In the strategy configuration screen for the strategy that you want to configure, use the "select segments" dropdown to add segments. - -![](/img/create-toggle-add-segment.png) - -### Add variants - -:::info - -This can be done at any point, during or after the creation of your flag. - -::: - -[Variants](../reference/strategy-variants) give you the ability to further target your users and split them into groups of your choosing, such as for A/B testing. On the flag overview page, select the variants tab. Use the "new variant" button to add the variants that you want. diff --git a/website/docs/how-to/how-to-download-login-history.mdx b/website/docs/how-to/how-to-download-login-history.mdx deleted file mode 100644 index 880eb78307..0000000000 --- a/website/docs/how-to/how-to-download-login-history.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: How to download your login history ---- - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `4.22+` - -::: - -[Login history](../reference/login-history.md) enables Unleash admins to audit login events and their respective information, including whether they were successful or not. - -## Step 1: Navigate to the login history page {#step-1} - -Navigate to the _login history_ page in the admin UI (available at the URL `/admin/logins`). Use the _settings_ button in the navigation menu and select "login history". - -![The admin UI navigation settings submenu with the login history item highlighted.](/img/login-history-1.png) - -## Step 2: Click the "Download login history" button {#step-2} - -Use the "download login history" button to proceed with the download of the login history as CSV. - -![The login history table with the "download login history" button highlighted.](/img/login-history-2.png) diff --git a/website/docs/how-to/how-to-enable-openapi.mdx b/website/docs/how-to/how-to-enable-openapi.mdx deleted file mode 100644 index 11c6bf177a..0000000000 --- a/website/docs/how-to/how-to-enable-openapi.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: How to enable OpenAPI and the Swagger UI ---- - -import Tabs from '@theme/Tabs'; - -import TabItem from '@theme/TabItem'; - -:::note Availability - -**Unleash version**: `4.13+` | **Unleash Proxy version**: `0.10+` - -::: - -Both Unleash and the Unleash proxy have included OpenAPI schemas and Swagger UIs for their APIs. The schemas can be used to get an overview of all API operations and to generate API clients using OpenAPI client generators. The Swagger UI lets you see and try out all the available API operations directly in your browser. - -To enable the OpenAPI documentation and the Swagger UI, you must start Unleash or the proxy with the correct configuration option. The following section shows you how. The methods are the same for both Unleash and the Unleash proxy, so the steps described in the next section will work for either. - -## Location of the OpenAPI spec - -Once you enable OpenAPI, you can find the specification in JSON format at `/docs/openapi.json` and the swagger UI at `/docs/openapi`. - -For instance, if you're running the Unleash server locally at `http://localhost:4242`, then - -- the JSON specification will be at `http://localhost:4242/docs/openapi.json` -- the Swagger UI will be at `http://localhost:4242/docs/openapi` - -Similarly, if you're running the Unleash proxy locally at `http://localhost:3000` (so that the proxy endpoint is at `http://localhost:3000/proxy`), then - -- the JSON specification will be at `http://localhost:3000/docs/openapi.json` -- the Swagger UI will be at `http://localhost:3000/docs/openapi` - -## Step 1: enable OpenAPI - -The OpenAPI spec and the Swagger UI can be turned on either via environment variables or via configuration options. Configuration options take precedence over environment variables. - -If you are using Unleash v5.2.0, OpenAPI is enabled by default. You still need to enable it for Unleash proxy. - -### Enable OpenAPI via environment variables - -To turn on OpenAPI via environment variables, set the `ENABLE_OAS` to `true` in the environment you're running the server in. - - - - - -```bash title="Enable OpenAPI via an environment variable" -export ENABLE_OAS=true -``` - - - - - -```bash title="Enable OpenAPI for Unleash via Docker" -docker run \ - // highlight-next-line - -e ENABLE_OAS=true \ # other variables omitted for brevity - unleashorg/unleash-server -``` - - - - - -```bash title="Enable OpenAPI for the Unleash proxy via Docker" -docker run \ - // highlight-next-line - -e ENABLE_OAS=true \ # other variables omitted for brevity - unleashorg/unleash-proxy -``` - - - - - -### Enable OpenAPI via configuration options - -The configuration option for enabling OpenAPI and the swagger UI is `enableOAS`. Set this option to `true`. - -The following examples have been shortened to show only the relevant configuration options. For more detailed instructions, refer to [our self-hosting guide](/using-unleash/deploy/getting-started). - - - - - -```js title="Enable OpenAPI for Unleash via configuration option" -const unleash = require('unleash-server'); - -unleash - .start({ - // ... Other options emitted for brevity - // highlight-next-line - enableOAS: true, - }) - .then((unleash) => { - console.log( - `Unleash started on http://localhost:${unleash.app.get('port')}`, - ); - }); -``` - - - - - -```js title="Enable OpenAPI for the Unleash proxy via configuration" -const port = 3000; - -const { createApp } = require('@unleash/proxy'); - -const app = createApp({ - // ... Other options elided for brevity - // highlight-next-line - enableOAS: true, -}); - -app.listen(port, () => - console.log(`Unleash Proxy listening on http://localhost:${port}/proxy`), -); -``` - - - - diff --git a/website/docs/how-to/how-to-environment-import-export.mdx b/website/docs/how-to/how-to-environment-import-export.mdx index fa27850774..2d10b67637 100644 --- a/website/docs/how-to/how-to-environment-import-export.mdx +++ b/website/docs/how-to/how-to-environment-import-export.mdx @@ -1,5 +1,5 @@ --- -title: Environment Import & Export +title: Environment import and export --- @@ -136,7 +136,7 @@ If change requests are enabled, any permissions for **Create activation strategi ## Environment import/export vs the instance import/export API -Environment import/export has some similarities to the [instance import/export API](./how-to-import-export), but they serve different purposes. +Environment import/export has some similarities to the [instance import/export API](/reference/environment-import-export), but they serve different purposes. The instance import/export API was designed to export all feature flags (optionally with strategies and projects) from one Unleash instance to another. When it was developed, Unleash had much fewer features than it does now. As such, the API lacks support for some of the more recent features in Unleash. diff --git a/website/docs/how-to/how-to-manage-public-invite-tokens.mdx b/website/docs/how-to/how-to-manage-public-invite-tokens.mdx deleted file mode 100644 index 77df6e2d8e..0000000000 --- a/website/docs/how-to/how-to-manage-public-invite-tokens.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: How to manage public invite tokens ---- - -[Public invite links](../reference/public-signup.mdx) let you invite new members to an Unleash instance. A key part of an invite link is the public invite token. This guide shows you how to use the Unleash admin UI to create, update, and delete public invite tokens. You can also [manage public signup tokens via the Unleash API](../reference/api/unleash/public-signup-tokens.tag.mdx). - -Only Unleash instance admins have the necessary permissions to create and manage public invite tokens. - -## Creating a token - -1. Navigate to the **users** page in Unleash and use the **create invite link** button - -![The settings menu in the Unleash nav bar with the "users" link highlighted.](/img/public-signup-step1.png) - -![The Unleash users page. There is a separate "create invite link" section above the list of users.](/img/public-signup-step2.png) - -2. Fill out the "create invite link" form and (optionally) copy the invite link. You can always get the link later. -![A short form with only one field: token expiry.](/img/public-signup-step3-create_link.png) - -![An "invite link created" modal. It contains an invite link that can be copied and some info on how to use it.](/img/public-signup-step4_link_Created.png) - -## Updating/Deleting a token - -1. Follow the steps in [the previous paragraph](#creating-a-token) to navigate to the users page. -2. When you have an active invite token, use the button labeled "update invite link". -3. Use the form to edit the expiry for the token or to delete it entirely. diff --git a/website/docs/how-to/how-to-run-the-unleash-proxy.mdx b/website/docs/how-to/how-to-run-the-unleash-proxy.mdx index 8440070deb..839a154a13 100644 --- a/website/docs/how-to/how-to-run-the-unleash-proxy.mdx +++ b/website/docs/how-to/how-to-run-the-unleash-proxy.mdx @@ -4,9 +4,9 @@ title: How to run the Unleash Proxy import ApiRequest from '@site/src/components/ApiRequest' -:::info Placeholders +:::warning -Placeholders in the code samples below are delimited by angle brackets (i.e. ``). You will need to replace them with the values that are correct in _your_ situation for the code samples to run properly. +Unleash Proxy is deprecated. Use [Unleash Edge](/reference/unleash-edge) instead. ::: diff --git a/website/docs/how-to/how-to-schedule-feature-releases.mdx b/website/docs/how-to/how-to-schedule-feature-releases.mdx index edbd402823..2119835931 100644 --- a/website/docs/how-to/how-to-schedule-feature-releases.mdx +++ b/website/docs/how-to/how-to-schedule-feature-releases.mdx @@ -1,5 +1,5 @@ --- -title: How to schedule feature releases +title: Schedule feature releases --- import ApiRequest from '@site/src/components/ApiRequest' diff --git a/website/docs/how-to/how-to-set-up-group-sso-sync.md b/website/docs/how-to/how-to-set-up-group-sso-sync.md index 499889c6fa..b05b30860c 100644 --- a/website/docs/how-to/how-to-set-up-group-sso-sync.md +++ b/website/docs/how-to/how-to-set-up-group-sso-sync.md @@ -1,5 +1,5 @@ --- -title: How to Set Up User Group SSO Syncing +title: Set up user group SSO syncing --- :::note Availability diff --git a/website/docs/how-to/how-to-setup-provisioning-with-entra.md b/website/docs/how-to/how-to-setup-provisioning-with-entra.md index a2a8cdab33..cd9768c5b2 100644 --- a/website/docs/how-to/how-to-setup-provisioning-with-entra.md +++ b/website/docs/how-to/how-to-setup-provisioning-with-entra.md @@ -1,5 +1,5 @@ --- -title: How to Setup Entra Provisioning +title: Set up Entra provisioning --- :::note Availability diff --git a/website/docs/how-to/how-to-setup-provisioning-with-okta.md b/website/docs/how-to/how-to-setup-provisioning-with-okta.md index fabac15412..2145dcba81 100644 --- a/website/docs/how-to/how-to-setup-provisioning-with-okta.md +++ b/website/docs/how-to/how-to-setup-provisioning-with-okta.md @@ -1,5 +1,6 @@ --- -title: How to Setup Okta Provisioning +title: Set up Okta provisioning +pagination_next: how-to/how-to-setup-provisioning-with-entra --- :::note Availability diff --git a/website/docs/how-to/how-to-setup-sso-keycloak-group-sync.md b/website/docs/how-to/how-to-setup-sso-keycloak-group-sync.md index 0687278ad4..abd4440157 100644 --- a/website/docs/how-to/how-to-setup-sso-keycloak-group-sync.md +++ b/website/docs/how-to/how-to-setup-sso-keycloak-group-sync.md @@ -1,5 +1,5 @@ --- -title: 'How to set up Keycloak and Unleash to sync user groups' +title: 'Set up user group syncing with Keycloack' --- :::note Availability diff --git a/website/docs/how-to/how-to-use-the-admin-api.md b/website/docs/how-to/how-to-use-the-admin-api.md deleted file mode 100644 index e4a001b577..0000000000 --- a/website/docs/how-to/how-to-use-the-admin-api.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: How to use the Admin API ---- - -This guide explains the steps required to using the Admin API. - -## Create API token - -First, you'll need to create a [personal access token](/reference/api-tokens-and-client-keys.mdx#personal-access-tokens). - -Please note that it may take up to 60 seconds for the new key to propagate to all Unleash instances due to eager caching. - -:::note - -If you need an API token to use in a client SDK you should create a client token instead, as these have fewer access rights. - -::: - -## Use Admin API - -Now that you have an access token with admin privileges, you can use it to make changes in your Unleash instance. - -In the example below we will use the [Unleash Admin API](/reference/api/legacy/unleash/admin/features.md) to enable the `checkout-flow` feature flag in `development` using curl. - -```sh -curl -X POST -H "Content-Type: application/json" \ - -H "Authorization: " \ - https://app.unleash-hosted.com/docs-demo/api/admin/projects/docs-project/features/checkout-flow/environments/development/on -``` - -We have now enabled the feature flag. We can also verify that it was actually changed by the API user by navigating to [Event Log](/reference/events#event-log) and filtering events for this feature flag. - -![Feature flag events showing that it was last updated by "admin-api".](/img/api_access_history.png) - -You can find the full documentation on everything the Unleash API supports in the [Unleash API documentation](/reference/api/legacy/unleash/admin/features.md). diff --git a/website/docs/quickstart.mdx b/website/docs/quickstart.mdx index 057b1ed844..7d1398f372 100644 --- a/website/docs/quickstart.mdx +++ b/website/docs/quickstart.mdx @@ -1,5 +1,6 @@ --- title: Quickstart +pagination_next: topics/what-is-a-feature-flag --- import Tabs from '@theme/Tabs'; @@ -125,6 +126,6 @@ For examples that show how to connect to Unleash in other programming languages, You have successfully connected Unleash to your application. To continue exploring, see the following resources: -- **Core concepts**: Learn about the [Unleash architecture](/understanding-unleash/unleash-overview), available [hosting options](/understanding-unleash/hosting-options), and other [reference documentation](/reference). +- **Core concepts**: Learn about the [Unleash architecture](/understanding-unleash/unleash-overview), available [hosting options](/understanding-unleash/hosting-options), and other [reference documentation](/reference/projects). - **Developer guides**: Explore feature flag [best practices](/topics/feature-flags/feature-flag-best-practices) and [language-specific tutorials](/feature-flag-tutorials/react). -- **Join the community**: Have questions or feedback? Join the [Unleash community on Slack](https://slack.unleash.run) to connect with other developers and the Unleash team. \ No newline at end of file +- **Join the community**: Have questions or feedback? Join the [Unleash community on Slack](https://slack.unleash.run) to connect with other developers and the Unleash team. diff --git a/website/docs/reference/activation-strategies.md b/website/docs/reference/activation-strategies.md index cf9a146ac7..31eed1e9ee 100644 --- a/website/docs/reference/activation-strategies.md +++ b/website/docs/reference/activation-strategies.md @@ -1,5 +1,5 @@ --- -title: Activation Strategies +title: Activation strategies --- import VideoContent from '@site/src/components/VideoContent.jsx' diff --git a/website/docs/reference/api-tokens-and-client-keys.mdx b/website/docs/reference/api-tokens-and-client-keys.mdx index 3d174bf90a..f291e41577 100644 --- a/website/docs/reference/api-tokens-and-client-keys.mdx +++ b/website/docs/reference/api-tokens-and-client-keys.mdx @@ -1,5 +1,5 @@ --- -title: API Tokens and Client Keys +title: API tokens and client keys pagination_next: reference/front-end-api --- @@ -137,8 +137,9 @@ be44368985f7fb3237c584ef86f3d6bdada42ddbd63a019d26955178 ## Create an API token -Depending on your [permissions](#api-token-permissions), you can create API tokens in the Unleash Admin UI in three ways: +Depending on your [permissions](#api-token-permissions), you can create API tokens in the Unleash Admin UI in four ways: - **Admin settings > Access control > API access**: for client or frontend tokens; requires the Admin root role, or a custom root role with [API token permissions](#api-token-permissions). +- **Admin settings > Service accounts > New service account**: for creating a service account and assigning a token. - **Settings > API access** inside a project: for project-specific client or frontend tokens; permitted for project Members or users with a [corresponding root role](#api-token-permissions). - **Profile > View profile settings > Personal API tokens**: for [personal access tokens](#personal-access-tokens). diff --git a/website/docs/reference/api/legacy/unleash/index.md b/website/docs/reference/api/legacy/unleash/index.md index 939f7cd58f..c8c86c13db 100644 --- a/website/docs/reference/api/legacy/unleash/index.md +++ b/website/docs/reference/api/legacy/unleash/index.md @@ -5,7 +5,7 @@ title: Legacy API Documentation :::caution -The docs in this category are legacy documentation. You should prefer to use the [Unleash OpenAPI docs](/reference/api/unleash) instead whenever possible. +These APIs have been deprecared. Wse the [Unleash OpenAPI docs](/api-overview) reference instead. ::: diff --git a/website/docs/reference/banners.md b/website/docs/reference/banners.md index 8322da73ef..3eb1384135 100644 --- a/website/docs/reference/banners.md +++ b/website/docs/reference/banners.md @@ -8,78 +8,61 @@ title: Banners ::: -Banners allow you to configure and display internal messages that all users of your Unleash instance can see and interact with. They are displayed at the top of the Unleash UI, and can be configured to be interactive. +Banners allow you to configure and display instance-wide messages to all users of your Unleash instance. These messages appear at the top of the Unleash UI and can be configured to be interactive. ![Banners table](/img/banners-table.png) -A common use case could be to have some pre-configured banners that you can enable when you need to communicate something to your users. For example, you could have a banner that you enable when you're doing maintenance on your Unleash instance, and another banner that you enable when you're running a survey. +A common use case for banners is to pre-configure messages that you can enable when needed. For example, you might have a banner for scheduled maintenance or another to announce a user survey. -In order to create and display a banner, you can follow the [how to create and display banners](../how-to/how-to-create-and-display-banners.md) guide. +Banners can be enabled or disabled at any time. -## Banner status +## Create a banner -Banners can be enabled or disabled at any time. For more information on how to enable or disable a banner, see the section on [displaying banners](../how-to/how-to-create-and-display-banners.md#displaying-banners). +To create a banner in the Admin UI, do the following: +1. Go to **Admin settings > Instance config > Banners**. +2. Click **New banner**. +3. Configure the status, type, icon, message, action, and whether the banner should be sticky. +4. Click **Add banner**. -| Option | Description | -| ----------- | -------------------------------------------------------------------------------- | -| **Enabled** | Whether the banner is currently displayed to all users of your Unleash instance. | +Once created, if the banner's status is set to enabled, the banner is immediately visible to all users in your Unleash instance. -## Configuration +## Configure the banner -Banners can be configured with the following options: +| Option | Description | Values / Format | +| :------ | :---------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------ | +| Type | Sets the banner's style (color and default icon). | `Information`, `Warning`, `Error`, `Success` | +| Status | Whether the banner is enabled and showing for all users of the instance. | Enabled or disabled | +| Icon | Icon displayed on the banner. | `Default` (matches banner type), `None` (hidden), `Custom` ([custom icon](#use-a-custom-icon)) | +| Message | Main text content of the banner. | Text format; supports [Markdown](https://www.markdownguide.org/basic-syntax/) | +| Action | Adds interactivity to the banner using a link or a dialog. | `None`, `Link`, `Dialog` | +| Sticky | Whether the banner remains fixed at the top of the Unleash UI, even when users scroll the page. | Enabled or disabled | -| Option | Description | -| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -| **Type** | The type of banner, which controls the banner's color and its icon, if using the default icon option. | -| **Icon** | The icon displayed on the banner. This can be the default for the banner type, a [custom icon](#custom-icon), or hidden by selecting "None". | -| **Message** | The banner's message. Supports [Markdown](https://www.markdownguide.org/basic-syntax/). | +### Use a custom icon -### Custom icon +To further personalize your banner, you can use any icon from the [Material Symbols](https://fonts.google.com/icons) library. -To further personalize your banner, you can configure it with a custom icon. +To use a custom icon: +1. In the banner configuration, select **Custom** from the **Icon** dropdown menu. +2. In the **Banner icon** field, enter the name of the desired Material Symbol. For example, to use the "Rocket Launch" icon, enter `rocket_launch`. -To use a custom icon in your banner: -1. Select "Custom" in the icon dropdown. -2. Enter the name of the desired [Material Symbol](https://fonts.google.com/icons). - - For example, for the "Rocket Launch" icon, enter `rocket_launch` in the custom icon field. +### Configure a link action -| Option | Description | -| --------------- | -------------------------------------------------------------------------------------------------------- | -| **Custom icon** | The custom icon to be displayed on the banner, using [Material Symbols](https://fonts.google.com/icons). | +This action displays a link on the banner that directs users to a specified URL. -## Banner action +- **Absolute URLs** (for example, `https://docs.getunleash.io/`) open in a new browser tab. +- **Relative URLs** (for example, `/admin/network`) open in the same tab. -You can set up an action for your banner: +| Option | Description | +| :----- | :---------------------------------------------- | +| URL | The URL the banner link should navigate to. | +| Text | The text displayed for the link on the banner. | -| Option | Description | -| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -| **Banner action** | The action activated when a user interacts with the banner link. Defaults to "None". Options include a [link](#link) or a [dialog](#dialog). | +### Configure a dialog action -### Link +This action displays a link on the banner that, when clicked, opens a dialog box with additional information. -When choosing the link action, a link will be displayed on the banner that directs users to a specified URL. - -The configured URL can be absolute, as in e.g. `https://docs.getunleash.io/`, or relative as in e.g. `/admin/network`. Absolute URLs will open in a new tab. - -| Option | Description | -| -------- | --------------------------------------------------------- | -| **URL** | The URL to open when the user uses the banner link. | -| **Text** | The text to display on the banner link. | - -### Dialog - -When opting for a dialog action, an interactable link appears on the banner which opens a dialog with additional information. - -| Option | Description | -| ------------------ | ------------------------------------------------------------------------------------------------------- | -| **Text** | The text to display on the banner link. | -| **Dialog title** | The title to display on the dialog. | -| **Dialog content** | The content to display on the dialog. Supports [Markdown](https://www.markdownguide.org/basic-syntax/). | - -## Sticky banner - -For added visibility, banners can be configured to be "sticky," ensuring they remain at the top of the Unleash UI, even after scrolling the page. This is useful for banners that you want to make sure that your users see and interact with. - -| Option | Description | -| ---------- | ---------------------------------------------------------- | -| **Sticky** | Whether the banner is sticky on the screen when scrolling. | +| Option | Description | +| :--------------- | :-------------------------------------------------------------------------- | +| Text | The text displayed for the link on the banner. | +| Dialog title | The title displayed at the top of the dialog box. | +| Dialog content | The main content displayed within the dialog box. Supports [Markdown](https://www.markdownguide.org/basic-syntax/). | diff --git a/website/docs/reference/change-requests.mdx b/website/docs/reference/change-requests.mdx index e414072df2..860d3a0c88 100644 --- a/website/docs/reference/change-requests.mdx +++ b/website/docs/reference/change-requests.mdx @@ -1,5 +1,5 @@ --- -title: Change Requests +title: Change requests --- import VideoContent from '@site/src/components/VideoContent.jsx'; diff --git a/website/docs/reference/command-menu.md b/website/docs/reference/command-menu.md index 3ec16eed41..c5d369b79e 100644 --- a/website/docs/reference/command-menu.md +++ b/website/docs/reference/command-menu.md @@ -1,5 +1,5 @@ --- -title: Command Menu +title: Command menu --- :::note Availability diff --git a/website/docs/reference/custom-activation-strategies.md b/website/docs/reference/custom-activation-strategies.md index 0bc3d4ffbc..8c8dd1cebf 100644 --- a/website/docs/reference/custom-activation-strategies.md +++ b/website/docs/reference/custom-activation-strategies.md @@ -1,5 +1,5 @@ --- -title: Custom Activation Strategies +title: Custom activation strategies --- **Custom activation strategies** let you define your own activation strategies to use with Unleash. When the [built-in activation strategies](../reference/activation-strategies.md) aren't enough, custom activation strategies are there to provide you with the flexibility you need. diff --git a/website/docs/how-to/how-to-import-export.mdx b/website/docs/reference/environment-import-export.mdx similarity index 99% rename from website/docs/how-to/how-to-import-export.mdx rename to website/docs/reference/environment-import-export.mdx index 24c7e90e7f..f28e662650 100644 --- a/website/docs/how-to/how-to-import-export.mdx +++ b/website/docs/reference/environment-import-export.mdx @@ -1,5 +1,5 @@ --- -title: 'Import & Export' +title: Import and export --- import ApiRequest from '@site/src/components/ApiRequest' diff --git a/website/docs/reference/feature-toggle-variants.md b/website/docs/reference/feature-toggle-variants.md index a02bcc7c48..69cfbb7c3e 100644 --- a/website/docs/reference/feature-toggle-variants.md +++ b/website/docs/reference/feature-toggle-variants.md @@ -1,5 +1,5 @@ --- -title: Feature Flag Variants (deprecated) +title: Feature flag variants (deprecated) --- :::warning diff --git a/website/docs/reference/feature-toggles.mdx b/website/docs/reference/feature-toggles.mdx index 7dd5a6fe30..3651126035 100644 --- a/website/docs/reference/feature-toggles.mdx +++ b/website/docs/reference/feature-toggles.mdx @@ -1,5 +1,5 @@ --- -title: Feature Flags +title: Feature flags pagination_next: reference/activation-strategies --- diff --git a/website/docs/reference/impression-data.md b/website/docs/reference/impression-data.md index 7dd3833d28..922218f567 100644 --- a/website/docs/reference/impression-data.md +++ b/website/docs/reference/impression-data.md @@ -1,5 +1,5 @@ --- -title: Impression Data +title: Impression data pagination_next: reference/events --- @@ -82,7 +82,7 @@ This table describes all the properties on the impression events: Impression data is strictly an **opt-in** feature and must be enabled on a **per-flag basis**. You can enable and disable it both when you create a flag and when you edit a flag. -You can enable impression data via the impression data flag in the admin UI's flag creation form. You can also go via the [the API, using the `impressionData` option](/reference/api/legacy/unleash/admin/features-v2#create-toggle). For more detailed instructions, see [the section on enabling impression data in the how-to guide for capturing impression data](../how-to/how-to-capture-impression-data#step-1). +You can enable impression data via the impression data flag in the admin UI's flag creation form. You can also go via the [the API, using the `impressionData` option](/reference/api/legacy/unleash/admin/features-v2#create-toggle). For more detailed instructions, see [the section on enabling impression data in the how-to guide for capturing impression data](/feature-flag-tutorials/use-cases/how-to-capture-impression-data). ![A feature flag creation form. At the end of the form is a heading that says "Impression data", a short paragraph that describes the feature, and a flag to opt in or out of it.](/img/create_feat_impression.png) diff --git a/website/docs/reference/login-history.md b/website/docs/reference/login-history.md deleted file mode 100644 index f764bbb72e..0000000000 --- a/website/docs/reference/login-history.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Login History ---- - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `4.22+` - -::: - -Unleash's login history lets you track login events in your Unleash instance, and whether the attempts were successful in logging in or not. - -![Login history table](/img/login-history-table.png) - -For each login event, it lists: - - - **Created**: When it happened - - **Username**: The username that was used - - **Authentication**: The authentication type that was used - - **IP address**: The IP address that made the attempt - - **Success**: Whether the attempt was successful or not - - **Failure reason**: If the attempt was not successful, the reason why - -You can see the failure reason by hovering over the "False" badge in the "Success" column. - -![Login history table failure reason](/img/login-history-table-fail.png) - -Use the login history to: - -- Audit login events in your Unleash instance -- Identify failed login attempts and investigate the cause -- Debug misconfigured authentication providers - -The login history is mutable: You can remove individual login events or clear the entire history by deleting all of them. - -Finally, the login history can be downloaded ([how do I download my Unleash login history](../how-to/how-to-download-login-history.mdx)) for external backups, audits, and the like. - -## Retention - -Events in the login history are retained for 336 hours (14 days). - -Events older than the retention period are automatically deleted, and you won't be able to recover them. If you would like to collect login event information past the retention period, we suggest periodically downloading the login history. diff --git a/website/docs/reference/login-history.mdx b/website/docs/reference/login-history.mdx new file mode 100644 index 0000000000..5a8c69dd06 --- /dev/null +++ b/website/docs/reference/login-history.mdx @@ -0,0 +1,31 @@ +--- +title: Login history +--- + +:::note Availability + +**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `4.22+` + +::: + +Login history helps you monitor access to your Unleash instance by tracking login events. It records both successful and unsuccessful login attempts. You can use this information to audit user activity within your instance, identify and investigate failed login attempts, or debug issues related to misconfigured authentication providers. + +You can find the login history in the Admin UI by navigating to **Admin settings > User config > Login history**. + +For each login event, it lists: + + - **Created**: The date and time the login event occurred. + - **Username**: The username used for the login attempt. + - **Authentication**: The type of authentication method used (for example, `password`, `open-id-connect`). + - **IP address**: The IP address from which the login attempt was made. + - **Success**: Whether the attempt was successful (`True` for successful). + - **Failure reason**: If a login attempt failed, this provides the reason. To see the failure reason, hover over the `False` label in the **Success** column. + +## Data retention + +Login history events are kept for 336 hours (14 days). Events older than this retention period are automatically deleted and cannot be recovered. If you need to retain login event information for a longer period, consider periodically [downloading login history](#download-login-history). + +## Download login history + +To download the login history, go to the login history page and click **Download login history**. + diff --git a/website/docs/reference/maintenance-mode.mdx b/website/docs/reference/maintenance-mode.mdx index 7351ede36c..fa58371165 100644 --- a/website/docs/reference/maintenance-mode.mdx +++ b/website/docs/reference/maintenance-mode.mdx @@ -1,5 +1,5 @@ --- -title: Maintenance Mode +title: Maintenance mode --- :::note Availability diff --git a/website/docs/reference/project-collaboration-mode.md b/website/docs/reference/project-collaboration-mode.md index f5c41aa7f0..49d3de12c3 100644 --- a/website/docs/reference/project-collaboration-mode.md +++ b/website/docs/reference/project-collaboration-mode.md @@ -1,5 +1,5 @@ --- -title: Project Collaboration Mode +title: Project collaboration mode --- :::note Availability diff --git a/website/docs/reference/public-signup.mdx b/website/docs/reference/public-signup.mdx index 38c3099839..88dc592aba 100644 --- a/website/docs/reference/public-signup.mdx +++ b/website/docs/reference/public-signup.mdx @@ -1,25 +1,15 @@ --- -title: Public Invite Links +title: Public invite links --- -Public invite links let you invite team members to your Unleash instance. Any user with an invite link can sign up to Unleash instance that created the link. The user will get the **viewer** role (refer to the [predefined roles_ section of the RBAC document](../reference/rbac.md#predefined-roles) for more information on roles). +Public invite links allow you to invite new team members to your Unleash instance. Any user who receives an invite link can use it to sign up for the Unleash instance that generated the link. When users sign up using an invite link, they are automatically assigned the [Viewer](../reference/rbac.md#predefined-roles) role. -User who follow the invite link are taken directly to the Unleash sign-up page, where they can create an account. +A token becomes active as soon as you create it, and remains valid until it expires or is deleted. Once a token is invalid, users can no longer sign up using an invite link containing that token. -Only **Unleash instance admins** can create public invite links. +You can have only one active invite token at any given time. If an active token already exists, you must delete it before you can [create a new one](#manage-the-public-invite-token). -![An Unleash signup form for new users](/img/public-invite_signup.png) +## Manage the public invite token -## Public sign-up tokens +As an Admin, you can create, update, and delete invite tokens through the Unleash Admin UI in **Admin settings > User config > Users > Create invite link**. -The most important part of a public sign-up link is the sign-up token. The token is added as the `invite` query parameter to the invite link. - -Each token has an **expiry date**. After this expiry date, the token will stop working and users can no longer sign up using an invite link with that token. - -## Creating, updating, and deleting tokens - -You can [create, update and delete tokens via the Unleash Admin UI](../how-to/how-to-manage-public-invite-tokens.mdx) or via the [Unleash API](../reference/api/unleash/public-signup-tokens.tag.mdx "Public sign-up tokens API documentation"). - -A token is active as soon as it's created and stops working as soon as it's deleted or expired. - -You can only have one active invite token at a time. If you already have an active token, you must delete it to create a new one. +Alternatively, you can use the [Admin API](../reference/api/unleash/public-signup-tokens.tag.mdx) to manage the public invite token. \ No newline at end of file diff --git a/website/docs/reference/rbac.md b/website/docs/reference/rbac.md index 7b2f70f584..a77d99c9a5 100644 --- a/website/docs/reference/rbac.md +++ b/website/docs/reference/rbac.md @@ -1,6 +1,6 @@ --- id: rbac -title: Role-based Access Control +title: Role-based access control --- :::note Availability @@ -40,8 +40,7 @@ own [custom root roles](#custom-root-roles) and [custom project roles](#custom-p Custom root roles let you define your own root roles with a specific set of root permissions. The roles can then be assigned to entities (users, service accounts, and groups) at the root level. This allows you to control access to -resources in a more precise, fine-grained way. For a step-by-step walkthrough of how to create and assign custom root -roles, refer to [_how to create and assign custom root roles_](../how-to/how-to-create-and-assign-custom-root-roles.md). +resources in a more precise, fine-grained way. Each custom root role consists of: @@ -49,6 +48,21 @@ Each custom root role consists of: - a **role description** (required) - a set of **root permissions** (required) +### Create and assign a custom root role + +To create a custom root role in the Admin UI, do the following: + +1. In **Admin settings > User config > Root roles**, click **New root role**. +2. Give the role a name and description and select all permissions you want to assign to the role. +3. Click **Add role** to save. + +Once you have the role set up, you can assign it a user: + +1. In **Admin settings > User config > Users**, select the user you want to assign the role to. +2. Click **Edit user**. +3. For **Role**, select the root role you want the user to have. +4. Click **Save**. + ### Root permissions You can assign the following root permissions: @@ -104,7 +118,7 @@ You can assign the following root permissions: | Change instance banners | Change instance [banners](./banners). | | Change maintenance mode state | Change [maintenance mode](./maintenance-mode) state. | | Update CORS settings | Update [CORS settings](./front-end-api#configure-cross-origin-resource-sharing-cors). | -| Read instance logs and login history | Read instance logs and [login history](./login-history.md). | +| Read instance logs and login history | Read instance logs and [login history](./login-history). | #### Integration permissions @@ -173,9 +187,7 @@ You can assign the following root permissions: Custom project roles let you define your own project roles with a specific set of project permissions down to the environment level. The roles can then be assigned to users in specific projects. All users have viewer access to all -projects and resources but must be assigned a project role to be allowed to edit a project's resources. For a -step-by-step walkthrough of how to create and assign custom project roles, see [_how to create and assign custom project -roles_](../how-to/how-to-create-and-assign-custom-project-roles). +projects and resources but must be assigned a project role to be allowed to edit a project's resources. Each custom project role consists of: @@ -183,10 +195,23 @@ Each custom project role consists of: - a **role description** (required) - a set of **project and environment permissions** (required) +### Create and assign a custom project role + +To create a custom project role in the Admin UI, do the following: + +1. In **Admin settings > User config > Project roles**, click **New project role**. +2. Give the role a name and description and select all permissions you want to assign to the role. +3. Click **Add role** to save. + +Once you have the role set up, you can assign it to individual users inside a project: + +1. In **Settings > User access**, click **Edit**. +2. For **Role**, select the custom project roles you want to apply. +3. Click **Save**. + ### Project permissions -You can assign the following project permissions. These permissions are valid across all of the [project](./projects)'s -environments. +You can assign the following project permissions. These permissions are valid across all of the [project](./projects)'s environments. #### API tokens | Permission Name | Description | @@ -278,31 +303,26 @@ To view a user’s permissions, go to **Admin settings > User config > Users**. ::: -User groups allow you to assign roles to a group of users within a project, rather than to a user directly. This allows -you to manage your user permissions more easily when there's lots of users in the system. For a guide on how to create -and manage user groups see [_how to create and manage user groups_](../how-to/how-to-create-and-manage-user-groups.md). +User groups allow you to manage user permissions efficiently by assigning roles to a collection of users instead of individually. This is particularly useful for projects with many users. -A user group consists of the following: +You can create and manage user groups in the Admin UI at **Admin settings > User config > Groups**. -- a **name** (required) -- a **description** (optional) -- a **list of users** (required) -- a list of SSO groups to sync from (optional) -- a root role associated with the group (optional; available in v5.1+) +When creating a user group, you can define the following: -Groups do nothing on their own. They must either be given a root role directly or a role on a project to assign -permissions. +- **Name**: A unique identifier for the group. +- **Description**: A brief explanation of the group's purpose. +- **Users**: A list of users who are members of this group. +- **SSO groups** to sync from: A list of single sign-on (SSO) groups to synchronize members from. +- **Root role**: A role assigned to the group at the root level. (Available in v5.1+) -Groups that do not have a root role need to be assigned a role on a project to be useful. You can assign both predefined -roles and custom project roles to groups. +Groups themselves do not grant permissions. To be functional, a group must either: +- Be assigned a root role. Members of this group will inherit the root role's permissions globally. +- Be assigned a role on a specific project. This grants the group's members the specified permissions within that project. You can assign both predefined and custom project roles to groups. -Any user that is a member of a group with a root role will inherit that root role's permissions on the root level. +A user can belong to multiple groups, and each group a user belongs to can have a different role assigned to it on a specific project. +If a user gains permissions for a project through multiple groups, they will inherit the most permissive set of permissions from all their assigned group roles for that project. -While a user can only have one role in a given project, a user may belong to multiple groups, and each of those groups -may be given a role on a project. In the case where a given user is given permissions through more than one group, the -user will inherit the most permissive permissions of all their groups in that project. - -## User group SSO integration +## Set up group SSO syncing :::note Availability @@ -310,23 +330,17 @@ user will inherit the most permissive permissions of all their groups in that pr ::: -User groups also support integration with your Single Sign-On (SSO) provider. This allows you to automatically assign -users to groups when they log in through SSO. Check out [_how to set up group SSO -sync_](../how-to/how-to-set-up-group-sso-sync.md) for a step-by-step walkthrough. +You can integrate user groups with your single sign-on (SSO) provider to automatically manage user assignments. +Note that this just-in-time process updates groups only when a user logs in, which differs from a full provisioning system like [SCIM](/how-to/how-to-setup-provisioning-with-okta) that syncs all user information proactively. -Users that have been added to a group through your SSO provider will be automatically removed next time they log in if -they've been removed from the SSO group. Users that have been manually added to the group will not be affected. +When a user logs in through SSO, they are automatically added to or removed from a user group based on their SSO group membership. Manually added users are not affected by the SSO sync. -To enable group sync, you'll need to set two fields in your SSO provider configuration options: +To enable group syncing, you configure two settings in your SSO provider configuration: -- **enable group syncing**: +- **Enable group syncing**: Turns the feature on. +- **Group field JSON path**: A JSON path expression that points to the field in your SSO token response that contains the user's groups. - Turns on group syncing. This is disabled by default. - -- **group field JSON path** - - A JSON path that should point to the groups field in your token response. This should match the exact field returned - by the provider. For example, if your token looks like this: +For example, if your token response looks like this, you would set the Group field JSON path to `groups`: ```json { @@ -345,12 +359,26 @@ To enable group sync, you'll need to set two fields in your SSO provider configu "nonce": "0394852-3190485-2490358" } ``` - You need to set the "Group Field JSON path" to "groups". + +After you enable syncing, you must link the SSO group names to the corresponding user group. Once you've enabled group syncing and set an appropriate path, you'll need to add the SSO group names to the Unleash group. This can be done by navigating to the Unleash group you want to enable sync for and adding the SSO group names to the "SSO group ID/name" property. +### Configure SSO group sync + +You must be an Admin in Unleash to perform these steps. + +1. Go to **Admin settings > Single sign-on**. Select your integration and click **Enable Group Syncing**. +2. in **Group Field JSON Path**, enter the JSON path for the groups field in your token response. +3. Click **Save**. +4. Go to **User config > Groups** and select the user group you want to sync and click **Edit**. +5. Add the exact SSO group names or IDs you want to link to the group. +6. Click **Save**. + +The next time a user who belongs to one of the linked SSO groups logs in, they are automatically added to the user group. If they have been removed from the SSO group, their access will be revoked on their next login. + [^1]: The project-level permission is still required for the [**create/overwrite variants ** (PUT)](/reference/api/unleash/overwrite-feature-variants) and [**update variants ** (PATCH)](/reference/api/unleash/patch-feature-variants) API endpoints, but it is not used for anything diff --git a/website/docs/reference/resource-limits.mdx b/website/docs/reference/resource-limits.mdx index 289cb4f208..32e0fc0ca8 100644 --- a/website/docs/reference/resource-limits.mdx +++ b/website/docs/reference/resource-limits.mdx @@ -1,5 +1,5 @@ --- -title: Resource Limits +title: Resource limits --- :::note Availability diff --git a/website/docs/reference/scim.md b/website/docs/reference/scim.md index f04b9b7d9a..f8c6f685c5 100644 --- a/website/docs/reference/scim.md +++ b/website/docs/reference/scim.md @@ -17,7 +17,7 @@ See our how to guides on setting up provisioning for [Okta](../how-to/how-to-set **Deprovisioning** -Deprovisioning can be setup on the provider side and allow for automatic clean up of users in a single place. This is especially useful if you're trying to manage the cost of your Unleash instance, since deprovisioned users will not count towards the seat count of your license. See our [how to guides](../how-to/provisioning) for specific provider configurations. +Deprovisioning can be setup on the provider side and allow for automatic clean up of users in a single place. This is especially useful if you're trying to manage the cost of your Unleash instance, since deprovisioned users will not count towards the seat count of your license. **Group syncing** diff --git a/website/docs/reference/service-accounts.md b/website/docs/reference/service-accounts.md index b4c3c3ce8b..6145828cf3 100644 --- a/website/docs/reference/service-accounts.md +++ b/website/docs/reference/service-accounts.md @@ -1,5 +1,5 @@ --- -title: Service Accounts +title: Service accounts --- :::note Availability diff --git a/website/docs/reference/strategy-variants.mdx b/website/docs/reference/strategy-variants.mdx index 69a6b7abe7..a615d91e48 100644 --- a/website/docs/reference/strategy-variants.mdx +++ b/website/docs/reference/strategy-variants.mdx @@ -1,5 +1,5 @@ --- -title: Strategy Variants +title: Strategy variants --- import VideoContent from '@site/src/components/VideoContent.jsx' diff --git a/website/docs/reference/technical-debt.md b/website/docs/reference/technical-debt.md index f0b42b0c56..21e81f8781 100644 --- a/website/docs/reference/technical-debt.md +++ b/website/docs/reference/technical-debt.md @@ -1,5 +1,5 @@ --- -title: Technical Debt +title: Technical debt pagination_next: reference/insights --- diff --git a/website/docs/reference/terraform.mdx b/website/docs/reference/terraform.mdx index ad96b78118..6090eef42a 100644 --- a/website/docs/reference/terraform.mdx +++ b/website/docs/reference/terraform.mdx @@ -65,7 +65,7 @@ resource "unleash_api_token" "client_token" { ### Single sign-on protocols - `unleash_oidc`: Manage your [OpenID Connect configuration](../how-to/how-to-add-sso-open-id-connect). -- `unleash_saml`: Manage your [SAML configuration](../how-to/sso). +- `unleash_saml`: Manage your [SAML configuration](../how-to/how-to-add-sso-saml). ### Context fields diff --git a/website/docs/reference/unleash-context.md b/website/docs/reference/unleash-context.md index f5f6aad232..9a63c32ee4 100644 --- a/website/docs/reference/unleash-context.md +++ b/website/docs/reference/unleash-context.md @@ -1,8 +1,8 @@ --- -title: Unleash Context +title: Unleash context --- -The **Unleash Context** contains information related to the current feature flag request. Unleash uses this context to evaluate [activation strategies](activation-strategies) and [strategy constraints](../reference/activation-strategies#constraints) and to calculate [flag stickiness](../reference/stickiness). The Unleash Context is an important feature of all the [Unleash client SDKs](../reference/sdks). +The **Unleash context** contains information related to the current feature flag request. Unleash uses this context to evaluate [activation strategies](activation-strategies) and [strategy constraints](../reference/activation-strategies#constraints) and to calculate [flag stickiness](../reference/stickiness). The Unleash Context is an important feature of all the [Unleash client SDKs](../reference/sdks). ## Overview diff --git a/website/docs/reference/whats-new-v4.md b/website/docs/reference/whats-new-v4.md index 27a8340f2c..c5e13801d3 100644 --- a/website/docs/reference/whats-new-v4.md +++ b/website/docs/reference/whats-new-v4.md @@ -40,13 +40,11 @@ In version 4 we added support for [OpenID Connect](https://openid.net/connect/) In version 4 we improved the User Management and made it available for Unleash Open-Source and Unleash Enterprise. Starting in v4 all users accessing Unleash needs to exist in Unleash in order to gain access (because they need to have the proper permission from RBAC.) -[Read more](../how-to/how-to-add-users-to-unleash) - ### API access {#api-access} In version 4 we improved the API Access and made it available for Unleash Open-Source and Unleash Enterprise. Starting from Unleash v4 we require all SDKs to use an access token in order to connect to Unleash. -[Read more](../how-to/how-to-use-the-admin-api) +[Read more](../api-overview#admin-api) ### Custom stickiness {#custom-stickiness} diff --git a/website/docs/topics/feature-flags/feature-flag-best-practices.mdx b/website/docs/topics/feature-flags/feature-flag-best-practices.mdx index 9a90fe0989..2f789e38ed 100644 --- a/website/docs/topics/feature-flags/feature-flag-best-practices.mdx +++ b/website/docs/topics/feature-flags/feature-flag-best-practices.mdx @@ -2,6 +2,7 @@ title: "11 principles for building and scaling feature flag systems" description: Build a scalable, secure feature flag system with 11 key principles. Improve DevOps metrics, ensure reliability, and enhance developer experience." toc_max_heading_level: 2 +pagination_next: topics/feature-flags/best-practices-using-feature-flags-at-scale --- import VideoContent from '@site/src/components/VideoContent.jsx'; diff --git a/website/docs/topics/concepts/what-is-a-feature-flag.mdx b/website/docs/topics/what-is-a-feature-flag.mdx similarity index 97% rename from website/docs/topics/concepts/what-is-a-feature-flag.mdx rename to website/docs/topics/what-is-a-feature-flag.mdx index 2f02f55730..98523b37a5 100644 --- a/website/docs/topics/concepts/what-is-a-feature-flag.mdx +++ b/website/docs/topics/what-is-a-feature-flag.mdx @@ -2,6 +2,7 @@ title: What is a feature flag and why are feature flags used? slug: /what-is-a-feature-flag description: Feature flags let you control software features in real time, enabling safer deployments, better testing, and faster innovation. +toc_max_heading_level: 2 --- Feature flags allow you to release, test, and manage features and functionality across your application without changing the source code. Organizations use added control and flexibility to deliver more and higher quality features with reduced cost, time, and risk. @@ -56,7 +57,7 @@ Moreover, feature flags enable quick mitigation of issues by allowing teams to i Feature flags significantly accelerate operational release cycles by enabling rapid release, testing, and rollback of features. This speed allows teams to adopt a more action-oriented and experimental approach, quickly iterating on new ideas without the risk of complex code integrations or burdensome deployments. Even when multiple teams are working on overlapping components of complex applications, feature flags streamline the process by reducing dependencies and conflicts. -Additionally, automated feature flags can [dynamically enable or disable features based on user behavior or system events](../../reference/actions.md), further speeding up the adaptation process. By embracing a CI/CD (continuous integration and continuous deployment) workflow with feature flags, teams can deliver improvements to their applications more frequently and reliably, ensuring a faster, more agile development cycle. +Additionally, automated feature flags can [dynamically enable or disable features based on user behavior or system events](/reference/actions.md), further speeding up the adaptation process. By embracing a CI/CD (continuous integration and continuous deployment) workflow with feature flags, teams can deliver improvements to their applications more frequently and reliably, ensuring a faster, more agile development cycle. ### Enable testing and experimenting @@ -210,7 +211,7 @@ The risk and cost of building software the old way are too high. When developers Unleash is on a mission to make developers’ lives easier. Individual developers love Unleash because it removes the pain of testing and deploying new features so they have more time and energy to innovate. Unleash is trusted by thousands of companies in production including Visa, Wayfair, Lloyd’s Banking Group, and Samsung. While we serve the needs of some of the world’s largest and most security-conscious organizations, we are also rated the *Easiest to Use in Feature Management software* by G2. If you want to learn more about how to implement feature flags at scale, check out the following resources: -- [Feature Flag Tutorials](/feature-flag-tutorials) +- [Feature Flag Tutorials](/feature-flag-tutorials/use-cases/gradual-rollout) - [Best practices for using feature flags at scale](./topics/feature-flags/best-practices-using-feature-flags-at-scale) - [Best practices for building and scaling feature flags](./topics/feature-flags/feature-flag-best-practices) - [Try Unleash for Free](https://www.getunleash.io/pricing) \ No newline at end of file diff --git a/website/docs/troubleshooting.mdx b/website/docs/troubleshooting.mdx new file mode 100644 index 0000000000..0a67f67eae --- /dev/null +++ b/website/docs/troubleshooting.mdx @@ -0,0 +1,136 @@ +--- +title: Troubleshooting +toc_max_heading_level: 2 +--- + +This guide helps you troubleshoot various situations you might encounter when working with Unleash feature flags, including flags not being returned, users not being exposed as expected, unexpected A/B test results, and CORS errors. + +## My feature flag is not returned or my users are not exposed + +If a feature flag isn't being returned by the Frontend API or Edge, or if users are not being exposed to a flag you believe is enabled, consider the following. By default, these endpoints do not return feature flags that are not enabled to save bandwidth. + +### Initial checks + +#### Verify feature configuration +- Ensure the feature flag has an [activation strategy](/reference/activation-strategies) associated with it that will evaluate to `true` for your given context. +- Confirm that the feature flag has been **enabled** in the specific environment your client application is using. (ref: [enabling a feature flag](/reference/feature-toggles)) + +#### SDK `ready` event +- Ensure your application, especially frontend clients, waits for the SDK to emit the `ready` event before calling `isEnabled('feature-flag')` or `getVariant('feature-flag')`. Calling these functions too early might mean the client hasn't yet received the latest flag configurations from the server. + +### Token configuration + +#### Check token type +- To connect to the Frontend API or Edge, you **must** use a [Front-end API token](/reference/api-tokens-and-client-keys#frontend-tokens). Other token types will not work. + +#### Check token access +- The token's access configuration is **immutable after creation** and defines which feature flags it can access. The format of the token indicates its scope: + - **Access to all projects (current and future):** Tokens starting with `*:` (e.g., `*:production:xyz123etc...`) provide access to flags in the specified environment across all projects. + - **Access to a discrete list of projects:** Tokens starting with `[]:` (e.g., `[]:production:xyz123etc...`) grant access to a specific subset of projects in the given environment. You can see which projects a token has access to on the API Tokens page in the Unleash admin UI. + - **Single project access:** Tokens starting with a project name (e.g., `my_fullstack_app:production:xyz123etc...`) are restricted to that project and environment. + +### Context and stickiness + +#### Gradual rollout strategy and stickiness +- When using a **gradual rollout** strategy, pay close attention to the [stickiness](/reference/stickiness) configuration. +- If the context provided by your SDK during flag evaluation **does not include the field specified for stickiness** (e.g., `userId`, `sessionId`, or a custom field), the gradual rollout strategy will evaluate to `false`. Consequently, the flag (or the "on" state for that user) will not be returned by the API. + +### Using the Unleash playground + +- Feature activation strategies can be combined in complex ways. The [Unleash Playground](/reference/playground.mdx) is an invaluable tool. You can use an access token along with various context values (input via the UI) to simulate how a flag will be resolved for different users and scenarios, helping you verify your configuration. + +### Alternative: Using variants for disabled/enabled states + +If you need to know about a flag regardless of whether it's "on" or "off" for a user (e.g., for analytics or UI rendering logic), consider using variants: + +- First, enable the feature flag itself in the desired environment. +- Next, configure [strategy variants](/reference/strategy-variants) to represent "enabled" and "disabled" states. You can assign percentages to these variants (e.g., 50% "enabled", 50% "disabled"). + + ![Using enabled and disabled variants](/img/enabled-disabled-variants.png) + *This flag itself is enabled in development and adds a 50%/50% split between disabled/enabled variants. This is essentially the same as a gradual rollout of 50% but using variants.* + +- Then, in your SDK, use the `getVariant()` call (or equivalent) instead of `isEnabled()`. +- This approach can also be combined with more complex constraint-based targeting. + + ![Using enabled and disabled variants with constraints](/img/enabled-disabled-variants-complex.png) + *This flag returns an "enabled" variant for clients with a specific `semver` and performs a percentage split for the remaining clients.* + +## My A/B tests are producing unexpected results + +If your A/B tests or experiments are producing unexpected results: + +#### Prerequisite check +- First, ensure the feature flag is being returned correctly by following the guidance in the "[My feature flag is not returned or my users are not exposed](#my-feature-flag-is-not-returned-or-my-users-are-not-exposed)" section above. + +#### Verify gradual rollout percentage +- Check the rollout percentage of your [gradual rollout activation strategy](/reference/activation-strategies). If you intend to include 100% of your user base in the A/B/n test, ensure the rollout percentage is set to 100%. + +#### Check stickiness and context +- Revisit the [stickiness](/reference/stickiness) configuration. + - If using default stickiness, confirm that either `userId` or `sessionId` (or both, depending on your setup) is consistently provided in the Unleash context from your application. + - If the context provided during flag evaluation does not include the field used for stickiness, the gradual rollout strategy will evaluate to `false`, and the user will not be part of the A/B test population for that flag. + +#### Ensure variants are correctly configured +- Refer to the documentation on [feature flag variants](/reference/feature-toggle-variants). +- For a simple 50-50 A/B test, your variants should be configured accordingly (e.g., two variants, "A" and "B", with appropriate weighting or rollout distribution if not handled by a parent strategy). + + ![An example of variants configured for an A/B test](/img/troubleshooting-flag-abn-test-unexpected-result-variants.png) + +#### Double-check SDK code for variant handling +- Verify that your application code correctly handles the feature flag variant response. Consult your specific SDK's documentation. +- For example, using the [Unleash React SDK](/reference/sdks/react), you might follow the [check variants](/reference/sdks/react#check-variants) section. Given the example variants "A" and "B", your code might look like this: + + ```tsx + import { useVariant } from '@unleash/proxy-client-react'; + + export const TestComponent = () => { + const variant = useVariant('ab-test-flag'); // 'ab-test-flag' is your feature flag name + + if (variant.name === 'A') { + return ; + } else if (variant.name === 'B') { + return ; + } + // Fallback or default component if the flag is off or variant is not recognized + return ; + }; + ``` + +#### Use the Unleash playground +- If results are still unexpected, use the [Playground](/reference/playground.mdx) to simulate different user contexts and verify that the feature flag and its variants are resolving as intended. + +## My requests are blocked due to CORS issues + +Cross-Origin Resource Sharing (CORS) issues can prevent your client-side application from communicating with the Unleash API or Edge. Browsers enforce CORS as a security measure. If you see errors like "No 'Access-Control-Policy' header is present on the requested resource," it's likely a CORS misconfiguration. + +### Configuring CORS in the Unleash admin UI (for Unleash server) + +- Navigate to **Settings** in the Unleash Admin Dashboard. +- Select **CORS Origins**. +- Define the allowed origins (e.g., `https://your-app.com`). + - **For troubleshooting:** You can temporarily set the allowed origin to `*` (a single asterisk) to allow all origins. This helps confirm if CORS is the root cause. + - **Important Security Note:** Using `*` in production is generally discouraged. Always restrict origins to only those that require access. +- These settings can also be managed via the [Unleash API](https://docs.getunleash.io/reference/api/unleash). + +### Configuring CORS for Unleash Edge + +If you are using Unleash Edge, CORS headers are typically configured via command-line flags when starting the Edge instance: + +- To allow a specific origin: + ```bash + unleash-edge edge --cors-origin "[https://your-application.com](https://your-application.com)" + ``` +- You can specify multiple domains as a comma-separated list or by using the `--cors-origin` flag multiple times. +- Other CORS-related headers (e.g., `Access-Control-Allow-Headers`, `Access-Control-Allow-Methods`) can also be set via command-line arguments. Refer to the Unleash Edge deployment documentation for details. + +### Verifying the `Access-Control-Allow-Origin` header + +You can use the `curl` command-line tool to inspect the response headers from your Unleash instance and verify the CORS configuration. Replace `` and `` with your Unleash server URL and a relevant API endpoint (e.g., the frontend API endpoint). + + +## I don't see an Unleash feature in the Admin UI + +If a documented Unleash feature isn't showing up in your Admin UI, check the following: + +- Is the feature included in your [Unleash plan and version](/availability)? +- Is the feature in beta? If so, reach out to us to get early access. \ No newline at end of file diff --git a/website/docs/understanding-unleash/data-collection.md b/website/docs/understanding-unleash/data-collection.md index 3c15818a08..d986d0d910 100644 --- a/website/docs/understanding-unleash/data-collection.md +++ b/website/docs/understanding-unleash/data-collection.md @@ -1,5 +1,6 @@ --- title: Data and privacy +pagination_next: using-unleash/compliance/compliance-overview --- At Unleash, we prioritize the privacy and security of our customers' data. Our [architecture](/understanding-unleash/the-anatomy-of-unleash) ensures privacy by evaluating feature flags locally within the [client SDKs](/reference/sdks) or [Unleash Edge](/reference/unleash-edge), meaning no user data is shared with the Unleash instance. diff --git a/website/docs/understanding-unleash/hosting-options.mdx b/website/docs/understanding-unleash/hosting-options.mdx index 173448a7d8..1df9e812f1 100644 --- a/website/docs/understanding-unleash/hosting-options.mdx +++ b/website/docs/understanding-unleash/hosting-options.mdx @@ -130,4 +130,4 @@ Unleash Proxy has been deprecated, use [Unleash Edge](../reference/unleash-edge) ::: -If you are currently self-hosting Proxy, see our [Edge migration guide](https://github.com/Unleash/unleash-edge/blob/main/docs/migration-guide.md) for a guide on how to migrate. +If you are currently self-hosting [Proxy](../reference/unleash-proxy), see our [Edge migration guide](https://github.com/Unleash/unleash-edge/blob/main/docs/migration-guide.md) for a guide on how to migrate. diff --git a/website/docs/understanding-unleash/the-anatomy-of-unleash.mdx b/website/docs/understanding-unleash/the-anatomy-of-unleash.mdx index c2dbc8751a..193fc534d2 100644 --- a/website/docs/understanding-unleash/the-anatomy-of-unleash.mdx +++ b/website/docs/understanding-unleash/the-anatomy-of-unleash.mdx @@ -1,5 +1,5 @@ --- -title: The Anatomy of Unleash +title: Core concepts --- This guide's purpose is to give you a conceptual overview of how Unleash works. It covers the various components that exist within our system and how they interact with each other and with external applications. The diagrams help you understand the fundamental building blocks, such as [projects](../reference/projects), [environments](../reference/environments), [variants](../reference/strategy-variants) and of course, [feature flags](../reference/feature-toggles). diff --git a/website/docs/understanding-unleash/unleash-overview.md b/website/docs/understanding-unleash/unleash-overview.md index 8e7bb96966..4afa1a8cd4 100644 --- a/website/docs/understanding-unleash/unleash-overview.md +++ b/website/docs/understanding-unleash/unleash-overview.md @@ -1,5 +1,6 @@ --- -title: Unleash architecture +title: Unleash architecture overview +pagination_next: understanding-unleash/the-anatomy-of-unleash --- Unleash is designed for privacy, speed, and resilience, enabling feature flag evaluations to occur locally within your applications. The architecture provides: @@ -68,13 +69,13 @@ Beyond scalability, Unleash Edge also offers privacy and security benefits for c #### Client API -The [Client API](/reference/api/unleash/client) is the API used by server-side SDKs to fetch feature flag configurations and send SDK usage metrics to Unleash. +The [Client API](/api-overview#client-api) is the API used by server-side SDKs to fetch feature flag configurations and send SDK usage metrics to Unleash. #### Frontend API -The [Frontend API](/reference/api/unleash/frontend-api) is the API used by client-side SDKs to retrieve all enabled feature flags for a given [Unleash Context](/reference/unleash-context) and send SDK usage metrics to Unleash. +The [Frontend API](/api-overview#frontend-api) is the API used by client-side SDKs to retrieve all enabled feature flags for a given [Unleash Context](/reference/unleash-context) and send SDK usage metrics to Unleash. #### Admin API -The [Admin API](/reference/api/unleash) is an API layer for managing all aspects of your Unleash instance, including creating, updating, and deleting resources, such as feature flags, activation strategies, and environments. This API is used by the [Unleash Admin UI](#the-unleash-admin-ui) and other tools and [integrations](/reference/integrations). +The [Admin API](/api-overview#admin-api) is an API layer for managing all aspects of your Unleash instance, including creating, updating, and deleting resources, such as feature flags, activation strategies, and environments. This API is used by the [Unleash Admin UI](#the-unleash-admin-ui) and other tools and [integrations](/reference/integrations). | API | Used by | Available endpoints | |---------------|---------|---| diff --git a/website/docs/using-unleash/troubleshooting/https.md b/website/docs/using-unleash/deploy/https.md similarity index 64% rename from website/docs/using-unleash/troubleshooting/https.md rename to website/docs/using-unleash/deploy/https.md index 84186f6517..9785fcbc3c 100644 --- a/website/docs/using-unleash/troubleshooting/https.md +++ b/website/docs/using-unleash/deploy/https.md @@ -2,28 +2,29 @@ title: Configuring Unleash to run over HTTPS --- -Preferred methods for setting up HTTPS in self-hosted instances, from highly recommended to those requiring more caution: +This guide outlines several methods for enabling HTTPS in your self-hosted Unleash instance. -## Load Balancer +## Load balancer + +The recommended and simplest method is to use a load balancer. -The best choice is to use a load balancer. A load balancer from a cloud provider renews the HTTPS certificates for you and keeps the data safe when it moves between the internet and your server. Also, your cloud provider's private network between your load balancer and the application is already encrypted. ## Sidecar If you're using something like Kubernetes and need HTTPS to be handled right next to your Unleash app, use a sidecar pattern. + This method keeps the HTTPS handling separate from the Unleash application logic. Tools like [Istio](https://istio.io/), [Envoy](https://www.envoyproxy.io/), [HAProxy](https://www.haproxy.org/), or [Nginx](https://www.nginx.com/) can help by automatically updating certificates. ## Manual SSL termination in Unleash -Approach manual SSL termination within Unleash with caution. -This direct control method over the SSL setup adds complexity and a maintenance burden that can be challenging to manage. +Manually terminating SSL directly within the Unleash application should be avoided. This method introduces unnecessary complexity and high maintenance burden for managing certificates and ensuring secure configurations. -If you insist on having Unleash do HTTPS termination for you, you'll need to set that up yourself using: -* http://expressjs.com/en/5x/api.html#app.listen -* https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener +If you must configure Unleash to handle HTTPS termination directly, you'll need to set it up using: +- http://expressjs.com/en/5x/api.html#app.listen +- https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener Example: ```javascript diff --git a/website/docs/using-unleash/troubleshooting/cors.md b/website/docs/using-unleash/troubleshooting/cors.md deleted file mode 100644 index f604b6b3b5..0000000000 --- a/website/docs/using-unleash/troubleshooting/cors.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: My requests are being blocked by CORS ---- - -1. Make sure you've configured CORS access in Unleash admin UI settings as defined in the [Unleash CORS Policy docs](/reference/front-end-api#configure-cross-origin-resource-sharing-cors). These settings can be changed in the Unleash Dashboard under **Settings -> CORS Origins** or by using the [API](/reference/api/unleash/set-cors). Allowing all origins (using a single asterisk) will address this matter and is a great starting point when troubleshooting the behavior. -1. When receiving "**No 'Access-Control-Policy' header is present on the requested resource**", using the command `curl -I https:///` will allow us to verify that the response includes the header `Access-Control-Allow-Origin: *`. diff --git a/website/docs/using-unleash/troubleshooting/feature-not-available.md b/website/docs/using-unleash/troubleshooting/feature-not-available.md deleted file mode 100644 index c620a4bf73..0000000000 --- a/website/docs/using-unleash/troubleshooting/feature-not-available.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: I don't see a documented Unleash feature in my admin UI ---- - -Occasionally, users might come across a situation where a documented Unleash feature isn't visible in their admin UI. Here's how to troubleshoot this issue. - -You can usually find availability information in the feature's documentation page, displayed in a box like this: - -:::note Availability - -**Plan**: [Enterprise](https://www.getunleash.io/pricing) | **Version**: `5.5+` - -::: - -1. Check that the feature is available in your current Unleash version. For example, [Service accounts](/reference/service-accounts) are available in **Unleash 4.21** and later. If you're running a previous version, you'll need to update your Unleash instance. -2. Make sure the feature is available for your plan, as you may need to [upgrade your plan](https://www.getunleash.io/pricing) to access the feature. -3. If this is a beta feature, it may not be enabled for your Unleash instance. Here's how you can enable it: - - If you have a **hosted** Unleash instance and you'd like early access to the new feature, reach out to us so we can enable it for you. - - If you're running a **self-hosted** Unleash instance, make sure you've enabled the feature in your Unleash configuration. Usually, this involves setting the correct environment variable. You can check the current flags and respective environment variables in your version's [src/lib/types/experimental.ts](https://github.com/Unleash/unleash/blob/main/src/lib/types/experimental.ts). Setting this variable may look something like `UNLEASH_EXPERIMENTAL_NEW_FEATURE=true`. - -If you've followed the above steps and still can't access the feature, please [contact us](https://slack.unleash.run) for further assistance. - -If you're currently using a beta feature, please [reach out to us](https://slack.unleash.run)! We would be thrilled if you could provide some feedback. diff --git a/website/docs/using-unleash/troubleshooting/flag-abn-test-unexpected-result.md b/website/docs/using-unleash/troubleshooting/flag-abn-test-unexpected-result.md deleted file mode 100644 index bf66182981..0000000000 --- a/website/docs/using-unleash/troubleshooting/flag-abn-test-unexpected-result.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: My feature flag is returning unexpected results in my A/B/n test ---- - -Occasionally, users might come across a situation where a feature flag is returning unexpected results when running an A/B/n test. - -The first thing to look into is your feature configuration. - -If you're unsure whether the feature flag is being properly returned, you can go through the steps in this troubleshooting guide before proceeding: [My feature flag is not returned in the Frontend API/Edge/Proxy](/using-unleash/troubleshooting/flag-not-returned.md). - -1. Verify the rollout percentage of your gradual rollout [activation strategy](/reference/activation-strategies). If you would like to target 100% of your user base on your A/B/n test, confirm that your rollout percentage is set to 100%. -2. Check the [stickiness](/reference/stickiness) value. If you're using default stickiness, confirm that either `userId` or `sessionId` are part of your context to ensure consistent results. Besides, if the provided context does not include the field used in the stickiness configuration, the gradual rollout strategy will be evaluated to `false` and therefore it will not be returned by the API. -3. Ensure that your variants are correctly configured. You can refer to [feature flag variants](/reference/feature-toggle-variants). For example, if you would like to run a simple 50-50 A/B test, then your variants should look similar to this: - -![An example of variants configured for an A/B test](/img/troubleshooting-flag-abn-test-unexpected-result-variants.png) - -4. Double check that your code is correctly handling the feature flag variant response. You can refer to your SDK documentation for more information on how to handle feature flag variants. For example, if you're using the [Unleash React SDK](/reference/sdks/react), you can follow the [check variants](/reference/sdks/react#check-variants) section of the documentation. Given the example variants above, this could look like the following: - -```tsx -import { useVariant } from '@unleash/proxy-client-react'; - -export const TestComponent = () => { - const variant = useVariant('ab-test-flag'); - - if (variant.name === 'A') { - return ; - } else if (variant.name === 'B') { - return ; - } - return ; -}; -``` - -Feature activation strategies can be combined in different ways, which may lead to complex scenarios. If you're still not seeing the results you expect, try using the [Playground](/reference/playground.mdx) to verify that the feature is properly configured and responding as expected. diff --git a/website/docs/using-unleash/troubleshooting/flag-exposure.md b/website/docs/using-unleash/troubleshooting/flag-exposure.md deleted file mode 100644 index 72046f7558..0000000000 --- a/website/docs/using-unleash/troubleshooting/flag-exposure.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: My feature flag is enabled but all/some of our users are not exposed to it ---- - -To confirm how users will have flags resolved, follow these steps: -1. Ensure your application is waiting for the `ready` event: It could be that frontend clients are calling `isEnabled('feature-flag')` before they have the response from the server. In this case, you should defer isEnabled calls until the client has emitted the `ready` event. -2. The [Unleash Playground](/reference/playground.mdx) was developed with this particular use case in mind. An access token can be used along with context values (passed in via the UI) to see how a flag will be resolved. -3. When using a **gradual rollout** strategy, be mindful of the **[stickiness](/reference/stickiness)** value. When evaluating a flag, if the provided context does not include the field used in the stickiness configuration, the gradual rollout strategy will be evaluated to `false`. diff --git a/website/docs/using-unleash/troubleshooting/flag-not-returned.md b/website/docs/using-unleash/troubleshooting/flag-not-returned.md deleted file mode 100644 index 042a94a14b..0000000000 --- a/website/docs/using-unleash/troubleshooting/flag-not-returned.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: My feature flag is not returned in the Frontend API/Edge/Proxy ---- - -By default, these endpoints will not return feature flags that are not enabled. This is mainly to save on bandwidth but it makes it a bit difficult to debug when features are not being returned. - -The first thing to look into is to validate that the feature is well configured and then check the token used from the SDK because it determines the set of accessible features. Last, verify that the context you're providing contains all the required data. - -1. Check that the feature is properly enabled: - 1. Verify that the feature has a strategy associated to it that will return true for your context (ref: [add a strategy](/how-to-create-feature-flag#step-2)) - 2. Verify that the feature has been enabled in the environment used by the client application (ref: [enabling a feature flag](/how-to-create-feature-flag#step-3)) -2. Check that your token is of the right [type](/reference/api-tokens-and-client-keys.mdx). To connect to the Frontend API, Edge or Proxy, you need to use a [Front-end token](/reference/api-tokens-and-client-keys#frontend-tokens) -3. Check that your token has access to the feature flag. The **token access configuration is immutable post-creation** and defines the set of features that the token can access. The different [parts of a token](/reference/api-tokens-and-client-keys#api-token-format) determine what projects and environment can be accessed: - 1. **Access to all projects (current and future)** - Tokens with a leading asterisk will provide access to all projects in a particular environment. For example, the token `*:production:xyz123etc...` will provide access to flags in the production environment of all projects. - 2. **Access to a discrete list of projects** - Tokens with a leading set of square brackets (empty) will be given access to a subset of projects in a particular environment. The token will look similar to the following: `[]:production:xyz123etc...`. Which projects the token has access to can be found on the API Tokens page in the Unleash admin UI. - 3. **Single project access** - Tokens that lead with a project name are bound to the specified project and environment. For example, `my_fullstack_app:production:xyz123etc...` will only have access to flags in the "my_fullstack_app" project as set in the production environment. -4. When using a **gradual rollout** strategy, be mindful of the **[stickiness](/reference/stickiness)** value. When evaluating a flag, if the provided context does not include the field used in the stickiness configuration, the gradual rollout strategy will be evaluated to `false` and therefore it will not be returned by the API. -5. Feature activation strategies can be combined in different ways, which may lead to complex scenarios. Try using the [Playground](/reference/playground.mdx) to verify that the feature is properly configured and responding as expected. - - -If you want to return a flag no matter if it's disabled or enabled you can move the disabled/enabled information into the [strategy variants](/reference/strategy-variants). - -![enabled_disabled_variants](/img/enabled-disabled-variants.png 'Using enabled and disabled variants') - -This flag itself is enabled in development and adds 50%/50% split between disabled/enabled variants. This is essentially the same as a gradual rollout of 50% but using variants. -Remember to use `getVariant` call instead of `isEnabled` call in your SDK. - -You can combine this approach with more complex constraint based targeting. - -![enabled_disabled_variants_complex](/img/enabled-disabled-variants-complex.png 'Using enabled and disabled variants with constraints') - -This flag returns enabled variant for the client with the explicit `semver` and performs percentage split for the remaining clients. diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 15da86264d..4be3cb8a97 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -470,10 +470,6 @@ class="header-github-link" from: '/reference/event-types', to: '/reference/events', }, - { - from: '/advanced/api_access', - to: '/how-to/how-to-use-the-admin-api', - }, { from: '/advanced/archived_toggles', to: '/reference/feature-toggles', @@ -508,22 +504,6 @@ class="header-github-link" from: '/advanced/stickiness', to: '/reference/stickiness', }, - { - from: '/advanced/sso-google', - to: '/how-to/how-to-add-sso-google', - }, - { - from: '/advanced/sso-open-id-connect', - to: '/how-to/how-to-add-sso-open-id-connect', - }, - { - from: '/advanced/sso-saml-keycloak', - to: '/how-to/how-to-add-sso-saml-keycloak', - }, - { - from: '/advanced/sso-saml', - to: '/how-to/how-to-add-sso-saml', - }, { from: '/advanced/enterprise-authentication', to: '/reference/sso', @@ -680,22 +660,10 @@ class="header-github-link" from: '/user_guide/unleash_context', to: '/reference/unleash-context', }, - { - from: '/user_guide/user-management', - to: '/how-to/how-to-add-users-to-unleash', - }, { from: '/user_guide/v4-whats-new', to: '/reference/whats-new-v4', }, - { - from: [ - '/user_guide/important-concepts', - '/tutorials/important-concepts', - '/reference/concepts/', - ], - to: '/reference', - }, { from: [ '/user_guide/quickstart', @@ -793,13 +761,6 @@ class="header-github-link" from: '/help', to: '/', }, - { - from: [ - '/topics/feature-flags/tutorials', - '/tutorials', - ], - to: '/feature-flag-tutorials', - }, { from: ['/tutorials/academy', '/unleash-academy'], to: '/unleash-academy/introduction', @@ -816,10 +777,6 @@ class="header-github-link" from: '/tutorials/academy-managing-unleash-for-devops', to: '/unleash-academy/managing-unleash-for-devops', }, - { - from: '/developer-guide', - to: '/contributing', - }, { from: [ '/tutorials/unleash-overview', @@ -848,34 +805,6 @@ class="header-github-link" ], to: '/understanding-unleash/data-collection', }, - - { - from: '/how-to/how-to-troubleshoot-flag-exposure', - to: '/using-unleash/troubleshooting/flag-exposure', - }, - { - from: '/how-to/how-to-troubleshoot-flag-not-returned', - to: '/using-unleash/troubleshooting/flag-not-returned', - }, - { - from: '/how-to/how-to-troubleshoot-cors', - to: '/using-unleash/troubleshooting/cors', - }, - { - from: '/how-to/how-to-troubleshoot-https', - to: '/using-unleash/troubleshooting/https', - }, - { - from: '/how-to/how-to-troubleshoot-feature-not-available', - to: '/using-unleash/troubleshooting/feature-not-available', - }, - { - from: [ - '/reference/deploy/import-export', - '/deploy/import_export', - ], - to: '/how-to/how-to-import-export', - }, { from: [ '/reference/deploy/environment-import-export', diff --git a/website/sidebars.ts b/website/sidebars.ts index 3c95750bb7..8c431385c7 100644 --- a/website/sidebars.ts +++ b/website/sidebars.ts @@ -37,34 +37,161 @@ const sidebars: SidebarsConfig = { }, ], documentation: [ - 'quickstart', { - label: 'Feature Flags Developer Guide', + label: 'Get started', type: 'category', link: { - type: 'generated-index', - title: 'Feature Flag Developer Guide', - description: - 'What are feature flags? And other concepts to get you started.', - slug: '/topics', + type: 'doc', + id: 'quickstart', }, items: [ { - label: 'Feature Flag Concepts', - collapsed: true, - type: 'category', - items: ['topics/concepts/what-is-a-feature-flag'], + type: 'doc', + label: 'Quickstart', + id: 'quickstart', }, { - label: 'Feature Flag Best Practices', + type: 'doc', + label: 'Introduction to feature flags', + id: 'topics/what-is-a-feature-flag', + }, + { + type: 'doc', + label: 'Unleash architecture overview', + id: 'understanding-unleash/unleash-overview', + }, + ], + }, + { + label: 'Core concepts', + type: 'category', + link: { + type: 'doc', + id: 'understanding-unleash/the-anatomy-of-unleash', + }, + items: [ + { + label: 'Projects and environments', + collapsed: true, + type: 'category', + link: { type: 'doc', id: 'reference/projects' }, + items: [ + 'reference/projects', + 'reference/project-collaboration-mode', + 'reference/environments', + 'reference/environment-import-export', + ], + }, + { + label: 'Feature flags and activation strategies', collapsed: true, type: 'category', link: { - type: 'generated-index', - title: 'Feature Flag Best Practices', - description: - 'Principles and recommendations for best practices of using feature flags.', - slug: 'topics/feature-flags/', + type: 'doc', + id: 'reference/feature-toggles', + }, + items: [ + 'reference/feature-toggles', + 'reference/activation-strategies', + 'reference/strategy-variants', + 'understanding-unleash/managing-constraints', + 'reference/segments', + 'reference/unleash-context', + 'reference/stickiness', + 'reference/release-templates', + ], + }, + { + label: 'Identity and access', + collapsed: true, + type: 'category', + link: { + type: 'doc', + id: 'reference/api-tokens-and-client-keys', + }, + items: [ + 'reference/api-tokens-and-client-keys', + 'reference/front-end-api', + 'reference/rbac', + 'reference/sso', + 'reference/scim', + 'reference/change-requests', + 'reference/public-signup', + ], + }, + { + label: 'Instance health and configuration', + collapsed: true, + type: 'category', + link: { + type: 'doc', + id: 'reference/technical-debt', + }, + items: [ + 'reference/technical-debt', + 'reference/insights', + 'reference/resource-limits', + ], + }, + { + label: 'Testing and monitoring', + collapsed: true, + type: 'category', + link: { + type: 'doc', + id: 'reference/impression-data', + }, + items: [ + 'reference/impression-data', + 'reference/events', + 'reference/playground', + 'reference/network-view', + ], + }, + { + label: 'Automation and integrations', + collapsed: true, + type: 'category', + link: { + type: 'doc', + id: 'reference/applications', + }, + items: [ + 'reference/applications', + 'reference/service-accounts', + 'reference/signals', + 'reference/actions', + ], + }, + { + label: 'Admin UI', + collapsed: true, + type: 'category', + link: { + type: 'doc', + id: 'reference/login-history', + }, + items: [ + 'reference/login-history', + 'reference/banners', + 'reference/command-menu', + 'reference/search-operators', + 'reference/maintenance-mode', + ], + }, + ], + }, + { + label: 'Tutorials and guides', + type: 'category', + items: [ + { + label: 'Feature management best practices', + collapsed: true, + type: 'category', + link: { + type: 'doc', + id: 'topics/feature-flags/feature-flag-best-practices', }, items: [ { @@ -85,61 +212,108 @@ const sidebars: SidebarsConfig = { ], }, { - label: 'Feature Flag Tutorials', + label: 'Development and release workflows', type: 'category', collapsed: true, link: { - type: 'generated-index', - title: 'Feature Flag Tutorials', - description: - 'Tutorials to implement feature flags with your framework.', - slug: 'feature-flag-tutorials', + type: 'doc', + id: 'feature-flag-tutorials/use-cases/how-to-create-feature-toggles', + }, + items: [ + { + type: 'doc', + id: 'feature-flag-tutorials/use-cases/how-to-create-feature-toggles', + }, + { + type: 'doc', + label: 'Schedule a feature release', + id: 'how-to/how-to-schedule-feature-releases', + }, + { + type: 'doc', + label: 'Trunk-based development', + id: 'feature-flag-tutorials/use-cases/trunk-based-development', + }, + { + type: 'doc', + label: 'Gradual rollout', + id: 'feature-flag-tutorials/use-cases/gradual-rollout', + }, + ], + }, + { + label: 'Enterprise-grade security and scale', + type: 'category', + collapsed: true, + link: { + type: 'doc', + id: 'feature-flag-tutorials/use-cases/user-management-access-controls', + }, + items: [ + { + type: 'doc', + label: 'User management, access controls, and auditing', + id: 'feature-flag-tutorials/use-cases/user-management-access-controls', + }, + { + type: 'doc', + label: 'Security and compliance', + id: 'feature-flag-tutorials/use-cases/security-compliance', + }, + { + type: 'doc', + label: 'Scaling Unleash', + id: 'feature-flag-tutorials/use-cases/scaling-unleash', + }, + ], + }, + { + label: 'Experimentation and analytics', + type: 'category', + collapsed: true, + link: { + type: 'doc', + id: 'feature-flag-tutorials/use-cases/a-b-testing', + }, + items: [ + { + type: 'doc', + label: 'A/B testing', + id: 'feature-flag-tutorials/use-cases/a-b-testing', + }, + { + type: 'doc', + label: 'Feature flags for AI', + id: 'feature-flag-tutorials/use-cases/ai', + }, + { + type: 'doc', + label: 'Use impression data for analytics', + id: 'feature-flag-tutorials/use-cases/how-to-capture-impression-data', + }, + ], + }, + { + type: 'category', + label: 'Language and framework examples', + collapsed: true, + link: { + type: 'doc', + id: 'feature-flag-tutorials/react/implementing-feature-flags', }, items: [ { type: 'category', - label: 'Use Cases', - collapsed: true, + label: 'React', link: { - type: 'generated-index', - title: 'Use Cases', - slug: 'use-cases', + type: 'doc', + id: 'feature-flag-tutorials/react/implementing-feature-flags', }, items: [ { type: 'doc', - label: 'Gradual Rollout', - id: 'feature-flag-tutorials/use-cases/gradual-rollout', - }, - { - type: 'doc', - label: 'A/B Testing', - id: 'feature-flag-tutorials/use-cases/a-b-testing', - }, - { - type: 'doc', - label: 'Feature Flags for AI', - id: 'feature-flag-tutorials/use-cases/ai', - }, - { - type: 'doc', - label: 'Trunk-Based Development', - id: 'feature-flag-tutorials/use-cases/trunk-based-development', - }, - { - type: 'doc', - label: 'User Management, Access Controls, and Auditing', - id: 'feature-flag-tutorials/use-cases/user-management-access-controls', - }, - { - type: 'doc', - label: 'Security and Compliance', - id: 'feature-flag-tutorials/use-cases/security-compliance', - }, - { - type: 'doc', - label: 'Scaling Unleash', - id: 'feature-flag-tutorials/use-cases/scaling-unleash', + label: 'Examples', + id: 'feature-flag-tutorials/react/examples', }, { type: 'doc', @@ -150,183 +324,156 @@ const sidebars: SidebarsConfig = { }, { type: 'category', - label: 'Languages and Frameworks', - collapsed: true, + label: 'Java', link: { - type: 'generated-index', - title: 'Languages and Frameworks', - slug: 'languages-and-frameworks', + type: 'doc', + id: 'feature-flag-tutorials/java/implementing-feature-flags', }, items: [ { - type: 'category', - label: 'React', - link: { - type: 'doc', - id: 'feature-flag-tutorials/react/implementing-feature-flags', - }, - items: [ - { - type: 'doc', - label: 'Examples', - id: 'feature-flag-tutorials/react/examples', - }, - ], - }, - { - type: 'category', - label: 'Python', - link: { - type: 'doc', - id: 'feature-flag-tutorials/python/implementing-feature-flags', - }, - items: [ - { - type: 'doc', - label: 'Python Examples', - id: 'feature-flag-tutorials/python/examples', - }, - { - type: 'doc', - label: 'Django Tutorial', - id: 'feature-flag-tutorials/django/implementing-feature-flags-django', - }, - { - type: 'doc', - label: 'Django Examples', - id: 'feature-flag-tutorials/django/django-examples', - }, - ], - }, - { - type: 'category', - label: 'Java', - link: { - type: 'doc', - id: 'feature-flag-tutorials/java/implementing-feature-flags', - }, - items: [ - { - type: 'doc', - label: 'Spring Boot', - id: 'feature-flag-tutorials/java/spring-boot-implementing-feature-flags', - }, - { - type: 'doc', - label: 'Spring Boot Examples', - id: 'feature-flag-tutorials/java/spring-boot-examples', - }, - ], - }, - { - type: 'category', - label: 'Ruby', - link: { - type: 'doc', - id: 'feature-flag-tutorials/ruby/implementing-feature-flags-ruby', - }, - items: [ - { - type: 'doc', - label: 'Ruby Examples', - id: 'feature-flag-tutorials/ruby/ruby-examples', - }, - { - type: 'doc', - label: 'Rails Tutorial', - id: 'feature-flag-tutorials/rails/implementing-feature-flags-rails', - }, - { - type: 'doc', - label: 'Rails Examples', - id: 'feature-flag-tutorials/rails/rails-examples', - }, - ], - }, - { - type: 'category', - label: 'Go', - link: { - type: 'doc', - id: 'feature-flag-tutorials/golang/implementing-feature-flags-golang', - }, - items: [ - { - type: 'doc', - label: 'Go Examples', - id: 'feature-flag-tutorials/golang/golang-examples', - }, - ], - }, - { - type: 'category', - label: '.NET', - link: { - type: 'doc', - id: 'feature-flag-tutorials/dotnet/implementing-feature-flags-dotnet', - }, - items: [ - { - type: 'doc', - label: 'Examples', - id: 'feature-flag-tutorials/dotnet/dotnet-examples', - }, - ], - }, - { - type: 'category', - label: 'iOS', - link: { - type: 'doc', - id: 'feature-flag-tutorials/ios/implementing-feature-flags-ios', - }, - items: [ - { - type: 'doc', - label: 'Examples', - id: 'feature-flag-tutorials/ios/examples', - }, - ], - }, - { - type: 'category', - label: 'Rust', - link: { - type: 'doc', - id: 'feature-flag-tutorials/rust/implementing-feature-flags-rust', - }, - items: [ - { - type: 'doc', - label: 'Examples', - id: 'feature-flag-tutorials/rust/rust-examples', - }, - ], + type: 'doc', + label: 'Spring Boot', + id: 'feature-flag-tutorials/java/spring-boot-implementing-feature-flags', }, { type: 'doc', - label: 'JavaScript', - id: 'feature-flag-tutorials/javascript/implementing-feature-flags-js', + label: 'Spring Boot Examples', + id: 'feature-flag-tutorials/java/spring-boot-examples', + }, + ], + }, + { + type: 'category', + label: 'Python', + link: { + type: 'doc', + id: 'feature-flag-tutorials/python/implementing-feature-flags', + }, + items: [ + { + type: 'doc', + label: 'Python Examples', + id: 'feature-flag-tutorials/python/examples', }, { type: 'doc', - label: 'Serverless', - id: 'feature-flag-tutorials/serverless/implementing-feature-flags-in-aws-lambda', + label: 'Django Tutorial', + id: 'feature-flag-tutorials/django/implementing-feature-flags-django', }, { type: 'doc', - label: 'Flutter', - id: 'feature-flag-tutorials/flutter/a-b-testing', + label: 'Django Examples', + id: 'feature-flag-tutorials/django/django-examples', + }, + ], + }, + { + type: 'doc', + label: 'Next.js', + id: 'feature-flag-tutorials/nextjs/implementing-feature-flags-nextjs', + }, + { + type: 'category', + label: 'Go', + link: { + type: 'doc', + id: 'feature-flag-tutorials/golang/implementing-feature-flags-golang', + }, + items: [ + { + type: 'doc', + label: 'Go Examples', + id: 'feature-flag-tutorials/golang/golang-examples', + }, + ], + }, + { + type: 'doc', + label: 'JavaScript', + id: 'feature-flag-tutorials/javascript/implementing-feature-flags-js', + }, + { + type: 'category', + label: '.NET', + link: { + type: 'doc', + id: 'feature-flag-tutorials/dotnet/implementing-feature-flags-dotnet', + }, + items: [ + { + type: 'doc', + label: 'Examples', + id: 'feature-flag-tutorials/dotnet/dotnet-examples', + }, + ], + }, + { + type: 'category', + label: 'iOS', + link: { + type: 'doc', + id: 'feature-flag-tutorials/ios/implementing-feature-flags-ios', + }, + items: [ + { + type: 'doc', + label: 'Examples', + id: 'feature-flag-tutorials/ios/examples', + }, + ], + }, + { + type: 'doc', + label: 'Serverless', + id: 'feature-flag-tutorials/serverless/implementing-feature-flags-in-aws-lambda', + }, + { + type: 'category', + label: 'Rust', + link: { + type: 'doc', + id: 'feature-flag-tutorials/rust/implementing-feature-flags-rust', + }, + items: [ + { + type: 'doc', + label: 'Examples', + id: 'feature-flag-tutorials/rust/rust-examples', + }, + ], + }, + { + type: 'doc', + label: 'Flutter', + id: 'feature-flag-tutorials/flutter/a-b-testing', + }, + { + type: 'doc', + label: 'SvelteKit', + id: 'feature-flag-tutorials/sveltekit/implementing-feature-flags-sveltekit', + }, + { + type: 'category', + label: 'Ruby', + link: { + type: 'doc', + id: 'feature-flag-tutorials/ruby/implementing-feature-flags-ruby', + }, + items: [ + { + type: 'doc', + label: 'Ruby Examples', + id: 'feature-flag-tutorials/ruby/ruby-examples', }, { type: 'doc', - label: 'Next.js', - id: 'feature-flag-tutorials/nextjs/implementing-feature-flags-nextjs', + label: 'Rails Tutorial', + id: 'feature-flag-tutorials/rails/implementing-feature-flags-rails', }, { type: 'doc', - label: 'SvelteKit', - id: 'feature-flag-tutorials/sveltekit/implementing-feature-flags-sveltekit', + label: 'Rails Examples', + id: 'feature-flag-tutorials/rails/rails-examples', }, ], }, @@ -334,244 +481,171 @@ const sidebars: SidebarsConfig = { }, ], }, - { - label: 'Understanding Unleash', - collapsed: false, + label: 'SDKs', type: 'category', - link: { - type: 'generated-index', - title: 'Understanding Unleash', - description: - 'Documentation on how Unleash works, high-level architecture and important concepts.', - slug: 'understanding-unleash', - }, + link: { type: 'doc', id: 'reference/sdks/index' }, items: [ - 'understanding-unleash/unleash-overview', - 'understanding-unleash/the-anatomy-of-unleash', - 'understanding-unleash/managing-constraints', - 'understanding-unleash/hosting-options', - 'understanding-unleash/data-collection', { type: 'category', - link: { - type: 'generated-index', - title: 'Unleash Concepts', - description: - 'Documents describing the inner parts of Unleash.', - slug: '/reference', - }, - label: 'Unleash Concepts', + label: 'Server-side SDKs', items: [ { - label: 'Projects and Environments', - collapsed: false, - type: 'category', - link: { type: 'doc', id: 'reference/projects' }, - items: [ - 'reference/projects', - 'reference/project-collaboration-mode', - 'reference/environments', - ], + type: 'autogenerated', + dirName: 'generated/sdks/server-side', }, + ], + }, + { + type: 'category', + label: 'Client-side SDKs', + items: [ { - label: 'Feature Flags and Activation Strategies', - collapsed: false, - type: 'category', - link: { - type: 'doc', - id: 'reference/feature-toggles', - }, - items: [ - 'reference/feature-toggles', - 'reference/activation-strategies', - 'reference/strategy-variants', - 'reference/segments', - 'reference/unleash-context', - 'reference/stickiness', - 'reference/release-templates', - ], - }, - { - label: 'Access Controls', - collapsed: true, - type: 'category', - link: { - type: 'doc', - id: 'reference/api-tokens-and-client-keys', - }, - items: [ - 'reference/api-tokens-and-client-keys', - 'reference/front-end-api', - 'reference/rbac', - 'reference/sso', - 'reference/scim', - 'reference/change-requests', - 'reference/public-signup', - ], - }, - { - label: 'Instance Health and Configuration', - collapsed: true, - type: 'category', - link: { - type: 'doc', - id: 'reference/technical-debt', - }, - items: [ - 'reference/technical-debt', - 'reference/insights', - 'reference/resource-limits', - ], - }, - { - label: 'Testing and Monitoring', - collapsed: true, - type: 'category', - link: { - type: 'doc', - id: 'reference/impression-data', - }, - items: [ - 'reference/impression-data', - 'reference/events', - 'reference/playground', - 'reference/network-view', - ], - }, - { - label: 'Automation and Integrations', - collapsed: true, - type: 'category', - link: { - type: 'doc', - id: 'reference/applications', - }, - items: [ - 'reference/applications', - 'reference/service-accounts', - 'reference/signals', - 'reference/actions', - ], - }, - { - label: 'Admin UI', - collapsed: true, - type: 'category', - link: { - type: 'doc', - id: 'reference/login-history', - }, - items: [ - 'reference/login-history', - 'reference/banners', - 'reference/command-menu', - 'reference/search-operators', - 'reference/maintenance-mode', - ], + type: 'autogenerated', + dirName: 'generated/sdks/client-side', }, ], }, + { + type: 'link', + label: 'Community SDKs', + href: '/reference/sdks#community-sdks', + }, + ], + }, + { + label: 'API reference', + type: 'category', + link: { + type: 'doc', + id: 'api-overview', + }, + items: [ + docsSidebar, + { + 'System API': [ + 'reference/api/legacy/unleash/internal/prometheus', + 'reference/api/legacy/unleash/internal/health', + ], + '(Legacy Docs) Admin API': [ + 'reference/api/legacy/unleash/admin/addons', + 'reference/api/legacy/unleash/admin/context', + 'reference/api/legacy/unleash/admin/events', + 'reference/api/legacy/unleash/admin/features-v2', + 'reference/api/legacy/unleash/admin/feature-types', + 'reference/api/legacy/unleash/admin/features', + 'reference/api/legacy/unleash/admin/archive', + 'reference/api/legacy/unleash/admin/metrics', + 'reference/api/legacy/unleash/admin/projects', + 'reference/api/legacy/unleash/admin/segments', + 'reference/api/legacy/unleash/admin/state', + 'reference/api/legacy/unleash/admin/strategies', + 'reference/api/legacy/unleash/admin/tags', + 'reference/api/legacy/unleash/admin/user-admin', + ], + '(Legacy Docs) Client API': [ + 'reference/api/legacy/unleash/client/features', + 'reference/api/legacy/unleash/client/metrics', + 'reference/api/legacy/unleash/client/register', + ], + }, ], }, { - label: 'Using Unleash', - collapsed: false, type: 'category', + label: 'Unleash Edge', + collapsed: true, link: { - type: 'generated-index', - title: 'Using Unleash', - description: - 'Documentation on how to accomplish specific tasks when building with Unleash, including API and SDK documentation.', - slug: '/using-unleash', + type: 'doc', + id: 'generated/unleash-edge', }, + items: [ + 'generated/unleash-edge/concepts', + 'generated/unleash-edge/deploying', + 'generated/unleash-edge/benchmarking', + 'generated/unleash-edge/cli', + 'generated/unleash-edge/development-guide', + 'generated/unleash-edge/migration-guide', + ], + }, + { + label: 'Integrate and deploy', + type: 'category', items: [ { - label: 'APIs', - collapsed: true, + type: 'doc', + label: 'Unleash hosting options', + id: 'understanding-unleash/hosting-options', + }, + { type: 'category', link: { - title: 'Unleash Server APIs', - type: 'generated-index', - description: - 'Generated API docs based on the Unleash OpenAPI schema.', - slug: '/reference/api/unleash', + type: 'doc', + id: 'using-unleash/deploy/getting-started', + }, + label: 'Self-hosting Unleash', + items: [ + 'using-unleash/deploy/getting-started', + 'using-unleash/deploy/configuring-unleash', + 'using-unleash/deploy/upgrading-unleash', + 'using-unleash/deploy/license-keys', + 'using-unleash/deploy/https', + { + type: 'doc', + label: 'Synchronize Unleash instances', + id: 'how-to/how-to-synchronize-unleash-instances', + }, + ], + }, + { + label: 'Single sign-on', + type: 'category', + link: { + type: 'doc', + id: 'how-to/how-to-add-sso-open-id-connect', }, items: [ - docsSidebar, - { - 'System API': [ - 'reference/api/legacy/unleash/internal/prometheus', - 'reference/api/legacy/unleash/internal/health', - ], - '(Legacy Docs) Admin API': [ - 'reference/api/legacy/unleash/admin/addons', - 'reference/api/legacy/unleash/admin/context', - 'reference/api/legacy/unleash/admin/events', - 'reference/api/legacy/unleash/admin/features-v2', - 'reference/api/legacy/unleash/admin/feature-types', - 'reference/api/legacy/unleash/admin/features', - 'reference/api/legacy/unleash/admin/archive', - 'reference/api/legacy/unleash/admin/metrics', - 'reference/api/legacy/unleash/admin/projects', - 'reference/api/legacy/unleash/admin/segments', - 'reference/api/legacy/unleash/admin/state', - 'reference/api/legacy/unleash/admin/strategies', - 'reference/api/legacy/unleash/admin/tags', - 'reference/api/legacy/unleash/admin/user-admin', - ], - '(Legacy Docs) Client API': [ - 'reference/api/legacy/unleash/client/features', - 'reference/api/legacy/unleash/client/metrics', - 'reference/api/legacy/unleash/client/register', - ], - }, + 'how-to/how-to-add-sso-open-id-connect', + 'how-to/how-to-add-sso-saml', + 'how-to/how-to-add-sso-saml-keycloak', + 'how-to/how-to-add-sso-azure-saml', + 'how-to/how-to-setup-sso-keycloak-group-sync', + 'how-to/how-to-set-up-group-sso-sync', ], }, { - label: 'Application SDKs', + label: 'Automatic provisioning', type: 'category', - link: { type: 'doc', id: 'reference/sdks/index' }, + link: { + type: 'doc', + id: 'how-to/how-to-setup-provisioning-with-okta', + }, items: [ - { - type: 'category', - label: 'Server-side SDKs', - items: [ - { - type: 'autogenerated', - dirName: 'generated/sdks/server-side', - }, - ], - }, - { - type: 'category', - label: 'Client-side SDKs', - items: [ - { - type: 'autogenerated', - dirName: 'generated/sdks/client-side', - }, - ], - }, - { - type: 'link', - label: 'Community SDKs', - href: '/reference/sdks#community-sdks', - }, + 'how-to/how-to-setup-provisioning-with-okta', + 'how-to/how-to-setup-provisioning-with-entra', ], }, { - label: 'Integrations and automation', + label: 'Integrations', type: 'category', - link: { type: 'doc', id: 'reference/integrations/index' }, + link: { + type: 'doc', + id: 'reference/integrations/index', + }, items: [ 'reference/integrations/datadog', { - 'Jira Server': [ + type: 'category', + label: 'Jira server', + items: [ 'reference/integrations/jira-server-plugin-installation', 'reference/integrations/jira-server-plugin-usage', ], - 'Jira Cloud': [ + }, + { + type: 'category', + label: 'Jira cloud', + items: [ 'reference/integrations/jira-cloud-plugin-installation', 'reference/integrations/jira-cloud-plugin-usage', ], @@ -586,69 +660,21 @@ const sidebars: SidebarsConfig = { }, ], }, + ], + }, + { + label: 'Data privacy and compliance', + type: 'category', + link: { + type: 'doc', + id: 'understanding-unleash/data-collection', + }, + items: [ { - type: 'category', - link: { - type: 'doc', - id: 'using-unleash/deploy/getting-started', - }, - label: 'Self-hosting Unleash', - items: [ - 'using-unleash/deploy/getting-started', - 'using-unleash/deploy/configuring-unleash', - 'using-unleash/deploy/upgrading-unleash', - 'using-unleash/deploy/license-keys', - ], + type: 'doc', + label: 'Data collection and privacy', + id: 'understanding-unleash/data-collection', }, - { - label: 'Single sign-on', - items: [ - 'how-to/how-to-add-sso-open-id-connect', - 'how-to/how-to-add-sso-saml', - 'how-to/how-to-add-sso-saml-keycloak', - 'how-to/how-to-add-sso-azure-saml', - 'how-to/how-to-setup-sso-keycloak-group-sync', - ], - type: 'category', - link: { - type: 'generated-index', - title: 'How-to: Single sign-on', - description: 'Single sign-on guides.', - slug: '/how-to/sso', - }, - }, - { - label: 'Automatic provisioning', - items: [ - 'how-to/how-to-setup-provisioning-with-okta', - 'how-to/how-to-setup-provisioning-with-entra', - ], - type: 'category', - link: { - type: 'generated-index', - title: 'How to: Provisioning', - description: 'Provisioning how-to guides.', - slug: '/how-to/provisioning', - }, - }, - { - type: 'category', - label: 'Unleash Edge', - collapsed: true, - link: { - type: 'doc', - id: 'generated/unleash-edge', - }, - items: [ - 'generated/unleash-edge/concepts', - 'generated/unleash-edge/deploying', - 'generated/unleash-edge/benchmarking', - 'generated/unleash-edge/cli', - 'generated/unleash-edge/development-guide', - 'generated/unleash-edge/migration-guide', - ], - }, - 'generated/unleash-proxy', { type: 'category', label: 'Compliance', @@ -664,7 +690,7 @@ const sidebars: SidebarsConfig = { }, { type: 'doc', - label: 'SOC2 Type II', + label: 'SOC2 type II', id: 'using-unleash/compliance/soc2', }, { @@ -674,135 +700,38 @@ const sidebars: SidebarsConfig = { }, ], }, - { - label: 'Troubleshooting', - type: 'category', - link: { - type: 'generated-index', - title: 'How-to: troubleshooting', - description: - 'Troubleshooting common problems. If you want to suggest new items, please phrase the title as a concrete problem', - slug: '/using-unleash/troubleshooting', - }, - items: [ - 'using-unleash/troubleshooting/cors', - 'using-unleash/troubleshooting/https', - 'using-unleash/troubleshooting/feature-not-available', - 'using-unleash/troubleshooting/flag-exposure', - 'using-unleash/troubleshooting/flag-not-returned', - 'using-unleash/troubleshooting/flag-abn-test-unexpected-result', - ], - }, - { - label: 'How-to guides', - type: 'category', - link: { - type: 'generated-index', - title: 'How-to guides', - description: 'Step-by-step recipes for you to follow.', - slug: '/how-to', - }, - items: [ - { - label: 'Unleash API guides', - type: 'category', - link: { - type: 'generated-index', - title: 'How-to: Unleash API', - description: - 'Learn how to work with the Unleash API', - slug: '/how-to/api', - }, - items: [ - 'how-to/how-to-download-login-history', - 'how-to/how-to-use-the-admin-api', - 'how-to/how-to-enable-openapi', - ], - }, - { - label: 'Unleash Proxy guides', - type: 'category', - link: { - type: 'generated-index', - title: 'How-to: The Unleash Proxy', - description: - 'Learn how to work with the Unleash Proxy', - slug: '/how-to/proxy', - }, - items: ['how-to/how-to-run-the-unleash-proxy'], - }, - { - label: 'Feature flags, strategies, context', - type: 'category', - link: { - type: 'generated-index', - title: 'How-to: general Unleash tasks', - description: - 'Guides for how to perform general Unleash tasks.', - slug: '/how-to/misc', - }, - items: [ - 'how-to/how-to-capture-impression-data', - 'how-to/how-to-create-feature-toggles', - 'how-to/how-to-create-and-display-banners', - 'how-to/how-to-schedule-feature-releases', - 'how-to/how-to-synchronize-unleash-instances', - ], - }, - { - label: 'Environments', - type: 'category', - link: { - type: 'generated-index', - title: 'How-to: environments', - description: 'Environments how-to guides.', - slug: '/how-to/env', - }, - items: ['how-to/how-to-environment-import-export'], - }, - { - label: 'Users and permissions', - items: [ - 'how-to/how-to-add-users-to-unleash', - 'how-to/how-to-create-and-assign-custom-root-roles', - 'how-to/how-to-create-and-assign-custom-project-roles', - 'how-to/how-to-create-and-manage-user-groups', - 'how-to/how-to-set-up-group-sso-sync', - ], - type: 'category', - link: { - type: 'generated-index', - title: 'How-to: users and permissions', - description: - 'Users and permission how-to guides.', - slug: '/how-to/users-and-permissions', - }, - }, - ], - }, ], }, { - label: 'Contributing to Unleash', + label: 'Support and community', type: 'category', - collapsed: true, - link: { - type: 'generated-index', - title: 'Contributing to Unleash', - description: 'Learn how to contribute to unleash.', - slug: '/contributing', - }, items: [ - 'contributing/developer-guide', - 'contributing/frontend/overview', - 'contributing/backend/overview', { + type: 'doc', + id: 'troubleshooting', + label: 'Troubleshooting', + }, + { + label: 'Contribute to Unleash', type: 'category', - label: 'Architectural Decision Records', + collapsed: true, + link: { + type: 'doc', + id: 'contributing/developer-guide', + }, items: [ + 'contributing/developer-guide', + 'contributing/frontend/overview', + 'contributing/backend/overview', { - type: 'autogenerated', - dirName: 'contributing/ADRs', // '.' means the current docs folder + type: 'category', + label: 'Architectural decision records', + items: [ + { + type: 'autogenerated', + dirName: 'contributing/ADRs', + }, + ], }, ], }, diff --git a/website/src/components/Homepage/HomepageCards.tsx b/website/src/components/Homepage/HomepageCards.tsx index 789f6d9179..04dbaca4b8 100644 --- a/website/src/components/Homepage/HomepageCards.tsx +++ b/website/src/components/Homepage/HomepageCards.tsx @@ -18,7 +18,7 @@ const cardsData = [ description: 'Explore best practices and step-by-step tutorials to help you integrate Unleash into your stack.', icon: , - href: '/feature-flag-tutorials/use-cases/gradual-rollout', + href: '/topics/feature-flags/feature-flag-best-practices', }, { title: 'SDKs', diff --git a/website/src/theme/DocSidebar/Desktop/Content/index.tsx b/website/src/theme/DocSidebar/Desktop/Content/index.tsx index bac1a47d98..11444b6ceb 100644 --- a/website/src/theme/DocSidebar/Desktop/Content/index.tsx +++ b/website/src/theme/DocSidebar/Desktop/Content/index.tsx @@ -1,20 +1,16 @@ import Content from '@theme-original/DocSidebar/Desktop/Content'; import type ContentType from '@theme/DocSidebar/Desktop/Content'; import type { WrapperProps } from '@docusaurus/types'; +import Link from '@docusaurus/Link'; type Props = WrapperProps; export default function ContentWrapper(props: Props): JSX.Element { return ( <> - +
- + ); diff --git a/website/static/img/add-group-to-project-step-1.png b/website/static/img/add-group-to-project-step-1.png deleted file mode 100644 index 0a77f0114a..0000000000 Binary files a/website/static/img/add-group-to-project-step-1.png and /dev/null differ diff --git a/website/static/img/add-group-to-project-step-2.png b/website/static/img/add-group-to-project-step-2.png deleted file mode 100644 index ef682dd0d8..0000000000 Binary files a/website/static/img/add-group-to-project-step-2.png and /dev/null differ diff --git a/website/static/img/add-group-to-project-step-3.png b/website/static/img/add-group-to-project-step-3.png deleted file mode 100644 index cac53b1b3a..0000000000 Binary files a/website/static/img/add-group-to-project-step-3.png and /dev/null differ diff --git a/website/static/img/add-group-to-project-step-4.png b/website/static/img/add-group-to-project-step-4.png deleted file mode 100644 index 972ee84257..0000000000 Binary files a/website/static/img/add-group-to-project-step-4.png and /dev/null differ diff --git a/website/static/img/add-group-to-project-step-5.png b/website/static/img/add-group-to-project-step-5.png deleted file mode 100644 index 795aec4541..0000000000 Binary files a/website/static/img/add-group-to-project-step-5.png and /dev/null differ diff --git a/website/static/img/add-user-to-group-step-1.png b/website/static/img/add-user-to-group-step-1.png deleted file mode 100644 index b2e301c8a7..0000000000 Binary files a/website/static/img/add-user-to-group-step-1.png and /dev/null differ diff --git a/website/static/img/add-user-to-group-step-2.png b/website/static/img/add-user-to-group-step-2.png deleted file mode 100644 index 306d567974..0000000000 Binary files a/website/static/img/add-user-to-group-step-2.png and /dev/null differ diff --git a/website/static/img/add-user-to-group-step-3.png b/website/static/img/add-user-to-group-step-3.png deleted file mode 100644 index 2fc70fde3a..0000000000 Binary files a/website/static/img/add-user-to-group-step-3.png and /dev/null differ diff --git a/website/static/img/create-banners-display.png b/website/static/img/create-banners-display.png deleted file mode 100644 index c9b7cbc22a..0000000000 Binary files a/website/static/img/create-banners-display.png and /dev/null differ diff --git a/website/static/img/create-banners-step-1.png b/website/static/img/create-banners-step-1.png deleted file mode 100644 index dd0ee3d202..0000000000 Binary files a/website/static/img/create-banners-step-1.png and /dev/null differ diff --git a/website/static/img/create-banners-step-2.png b/website/static/img/create-banners-step-2.png deleted file mode 100644 index 37c2536e44..0000000000 Binary files a/website/static/img/create-banners-step-2.png and /dev/null differ diff --git a/website/static/img/create-banners-step-3.png b/website/static/img/create-banners-step-3.png deleted file mode 100644 index 172f04bce7..0000000000 Binary files a/website/static/img/create-banners-step-3.png and /dev/null differ diff --git a/website/static/img/create-cpr-step-1.png b/website/static/img/create-cpr-step-1.png deleted file mode 100644 index 4ac7b1a96c..0000000000 Binary files a/website/static/img/create-cpr-step-1.png and /dev/null differ diff --git a/website/static/img/create-cpr-step-2.png b/website/static/img/create-cpr-step-2.png deleted file mode 100644 index ae6496f703..0000000000 Binary files a/website/static/img/create-cpr-step-2.png and /dev/null differ diff --git a/website/static/img/create-cpr-step-3.png b/website/static/img/create-cpr-step-3.png deleted file mode 100644 index fcfb7c75c9..0000000000 Binary files a/website/static/img/create-cpr-step-3.png and /dev/null differ diff --git a/website/static/img/create-cpr-step-4.png b/website/static/img/create-cpr-step-4.png deleted file mode 100644 index de111e4203..0000000000 Binary files a/website/static/img/create-cpr-step-4.png and /dev/null differ diff --git a/website/static/img/create-crr-step-1.png b/website/static/img/create-crr-step-1.png deleted file mode 100644 index 1d1c7e8052..0000000000 Binary files a/website/static/img/create-crr-step-1.png and /dev/null differ diff --git a/website/static/img/create-crr-step-2.png b/website/static/img/create-crr-step-2.png deleted file mode 100644 index 9d7a0510f8..0000000000 Binary files a/website/static/img/create-crr-step-2.png and /dev/null differ diff --git a/website/static/img/create-crr-step-3.png b/website/static/img/create-crr-step-3.png deleted file mode 100644 index fb2044e0ba..0000000000 Binary files a/website/static/img/create-crr-step-3.png and /dev/null differ diff --git a/website/static/img/create-toggle-add-constraint.png b/website/static/img/create-toggle-add-constraint.png deleted file mode 100644 index 6c45ea3e67..0000000000 Binary files a/website/static/img/create-toggle-add-constraint.png and /dev/null differ diff --git a/website/static/img/create-toggle-add-segment.png b/website/static/img/create-toggle-add-segment.png deleted file mode 100644 index 566648c152..0000000000 Binary files a/website/static/img/create-toggle-add-segment.png and /dev/null differ diff --git a/website/static/img/create-toggle-add-strategy.png b/website/static/img/create-toggle-add-strategy.png deleted file mode 100644 index 72757fde0d..0000000000 Binary files a/website/static/img/create-toggle-add-strategy.png and /dev/null differ diff --git a/website/static/img/create-toggle-add-variants.png b/website/static/img/create-toggle-add-variants.png deleted file mode 100644 index f7b8c13fa3..0000000000 Binary files a/website/static/img/create-toggle-add-variants.png and /dev/null differ diff --git a/website/static/img/create-toggle-edit-strategy.png b/website/static/img/create-toggle-edit-strategy.png deleted file mode 100644 index 42ea54c9a1..0000000000 Binary files a/website/static/img/create-toggle-edit-strategy.png and /dev/null differ diff --git a/website/static/img/create-toggle-enable-env.png b/website/static/img/create-toggle-enable-env.png deleted file mode 100644 index 18cfe4d7f4..0000000000 Binary files a/website/static/img/create-toggle-enable-env.png and /dev/null differ diff --git a/website/static/img/create-toggle-new-toggle.png b/website/static/img/create-toggle-new-toggle.png deleted file mode 100644 index fce535f725..0000000000 Binary files a/website/static/img/create-toggle-new-toggle.png and /dev/null differ diff --git a/website/static/img/create-ug-step-1.png b/website/static/img/create-ug-step-1.png deleted file mode 100644 index 1375f0241d..0000000000 Binary files a/website/static/img/create-ug-step-1.png and /dev/null differ diff --git a/website/static/img/create-ug-step-2.png b/website/static/img/create-ug-step-2.png deleted file mode 100644 index 642c4c9f2e..0000000000 Binary files a/website/static/img/create-ug-step-2.png and /dev/null differ diff --git a/website/static/img/create-ug-step-3.png b/website/static/img/create-ug-step-3.png deleted file mode 100644 index 256bc6b9fa..0000000000 Binary files a/website/static/img/create-ug-step-3.png and /dev/null differ diff --git a/website/static/img/create-ug-step-4.png b/website/static/img/create-ug-step-4.png deleted file mode 100644 index e7e64cf285..0000000000 Binary files a/website/static/img/create-ug-step-4.png and /dev/null differ diff --git a/website/static/img/login-history-1.png b/website/static/img/login-history-1.png deleted file mode 100644 index e4a7377ed8..0000000000 Binary files a/website/static/img/login-history-1.png and /dev/null differ diff --git a/website/static/img/login-history-2.png b/website/static/img/login-history-2.png deleted file mode 100644 index f4561bc3de..0000000000 Binary files a/website/static/img/login-history-2.png and /dev/null differ diff --git a/website/static/img/login-history-table-fail.png b/website/static/img/login-history-table-fail.png deleted file mode 100644 index d009b319e9..0000000000 Binary files a/website/static/img/login-history-table-fail.png and /dev/null differ diff --git a/website/static/img/login-history-table.png b/website/static/img/login-history-table.png deleted file mode 100644 index 2f2f4e6783..0000000000 Binary files a/website/static/img/login-history-table.png and /dev/null differ diff --git a/website/static/img/public-signup-step1.png b/website/static/img/public-signup-step1.png deleted file mode 100644 index f99f121c0f..0000000000 Binary files a/website/static/img/public-signup-step1.png and /dev/null differ diff --git a/website/static/img/public-signup-step2.png b/website/static/img/public-signup-step2.png deleted file mode 100644 index ad0755f699..0000000000 Binary files a/website/static/img/public-signup-step2.png and /dev/null differ diff --git a/website/static/img/public-signup-step3-create_link.png b/website/static/img/public-signup-step3-create_link.png deleted file mode 100644 index ee1730ed20..0000000000 Binary files a/website/static/img/public-signup-step3-create_link.png and /dev/null differ diff --git a/website/static/img/public-signup-step4_link_Created.png b/website/static/img/public-signup-step4_link_Created.png deleted file mode 100644 index b81dde532e..0000000000 Binary files a/website/static/img/public-signup-step4_link_Created.png and /dev/null differ diff --git a/website/static/img/remove-user-from-group-step-1.png b/website/static/img/remove-user-from-group-step-1.png deleted file mode 100644 index afb40186a2..0000000000 Binary files a/website/static/img/remove-user-from-group-step-1.png and /dev/null differ diff --git a/website/static/img/remove-user-from-group-step-2.png b/website/static/img/remove-user-from-group-step-2.png deleted file mode 100644 index 14b1682cd6..0000000000 Binary files a/website/static/img/remove-user-from-group-step-2.png and /dev/null differ diff --git a/website/static/img/user_admin-add-user.jpg b/website/static/img/user_admin-add-user.jpg deleted file mode 100644 index 24ec6e1b92..0000000000 Binary files a/website/static/img/user_admin-add-user.jpg and /dev/null differ diff --git a/website/static/img/user_admin_add_user_modal.png b/website/static/img/user_admin_add_user_modal.png deleted file mode 100644 index 8c7302b160..0000000000 Binary files a/website/static/img/user_admin_add_user_modal.png and /dev/null differ diff --git a/website/static/img/user_admin_list_button.png b/website/static/img/user_admin_list_button.png deleted file mode 100644 index d8a0f00ece..0000000000 Binary files a/website/static/img/user_admin_list_button.png and /dev/null differ diff --git a/website/vercel.json b/website/vercel.json index cf2644f1d1..f49394785d 100644 --- a/website/vercel.json +++ b/website/vercel.json @@ -82,13 +82,13 @@ "permanent": true }, { - "source": "/api/open_api", - "destination": "/reference/api/unleash", + "source": "/reference/api/unleash", + "destination": "/api-overview", "permanent": true }, { - "source": "/advanced/api_access", - "destination": "/how-to/how-to-use-the-admin-api", + "source": "/api/open_api", + "destination": "/api-overview", "permanent": true }, { @@ -131,11 +131,46 @@ "destination": "/reference/feature-toggles#set-a-naming-pattern", "permanent": true }, + { + "source": "/how-to/", + "destination": "/what-is-a-feature-flag", + "permanent": true + }, + { + "source": "/how-to/how-to-add-users-to-unleash", + "destination": "/reference/rbac", + "permanent": true + }, + { + "source": "/how-to/how-to-import-export", + "destination": "/reference/environment-import-export", + "permanent": true + }, + { + "source": "/how-to/users-and-permissions", + "destination": "/reference/rbac", + "permanent": true + }, + { + "source": "/how-to/env", + "destination": "/reference/environments", + "permanent": true + }, + { + "source": "/how-to/how-to-create-and-manage-user-groups", + "destination": "/reference/rbac#user-groups", + "permanent": true + }, { "source": "/how-to/how-to-clone-environments", "destination": "/reference/environments#clone-an-environment", "permanent": true }, + { + "source": "/how-to-create-and-display-banners", + "destination": "/reference/banners", + "permanent": true + }, { "source": "/toggle_variants", "destination": "/reference/feature-toggle-variants", @@ -461,11 +496,6 @@ "destination": "/reference/unleash-context", "permanent": true }, - { - "source": "/user_guide/user-management", - "destination": "/how-to/how-to-add-users-to-unleash", - "permanent": true - }, { "source": "/user_guide/v4-whats-new", "destination": "/reference/whats-new-v4", @@ -618,12 +648,22 @@ }, { "source": "/topics/feature-flags/tutorials", - "destination": "/feature-flag-tutorials", + "destination": "/feature-flag-tutorials/react", + "permanent": true + }, + { + "source": "/reference", + "destination": "/reference/projects", + "permanent": true + }, + { + "source": "/feature-flags-tutorials", + "destination": "/feature-flag-tutorials/react", "permanent": true }, { "source": "/tutorials", - "destination": "/feature-flag-tutorials", + "destination": "/feature-flag-tutorials/react", "permanent": true }, { @@ -668,7 +708,17 @@ }, { "source": "/developer-guide", - "destination": "/contributing", + "destination": "/contributing/developer-guide", + "permanent": true + }, + { + "source": "/how-to/how-to-capture-impression-data", + "destination": "/feature-flag-tutorials/use-cases/how-to-capture-impression-data", + "permanent": true + }, + { + "source": "/how-to/how-to-use-the-admin-api", + "destination": "/api-overview#admin-api", "permanent": true }, { @@ -686,6 +736,16 @@ "destination": "/understanding-unleash/managing-constraints", "permanent": true }, + { + "source": "/how-to/how-to-enable-openapi", + "destination": "/api-overview", + "permanent": true + }, + { + "source": "/how-to/api", + "destination": "/api-overview", + "permanent": true + }, { "source": "/topics/managing-constraints", "destination": "/understanding-unleash/managing-constraints", @@ -701,6 +761,21 @@ "destination": "/understanding-unleash/the-anatomy-of-unleash", "permanent": true }, + { + "source": "/understanding-unleash", + "destination": "/understanding-unleash/unleash-overview", + "permanent": true + }, + { + "source": "/reference", + "destination": "/reference/projects", + "permanent": true + }, + { + "source": "/using-unleash", + "destination": "/api-overview", + "permanent": true + }, { "source": "/tutorials/proxy-hosting", "destination": "/understanding-unleash/hosting-options", @@ -762,18 +837,48 @@ "permanent": true }, { - "source": "/how-to/how-to-troubleshoot-cors", - "destination": "/using-unleash/troubleshooting/cors", + "source": "/how-to/how-to-troubleshoot-flag-not-returned", + "destination": "/using-unleash/troubleshooting/flag-not-returned", "permanent": true }, { - "source": "/how-to/how-to-troubleshoot-https", - "destination": "/using-unleash/troubleshooting/https", + "source": "/using-unleash/troubleshooting/cors", + "destination": "troubleshooting", "permanent": true }, { - "source": "/how-to/how-to-troubleshoot-feature-not-available", - "destination": "/using-unleash/troubleshooting/feature-not-available", + "source": "/using-unleash/troubleshooting/flag-abn-unexpected-results", + "destination": "troubleshooting", + "permanent": true + }, + { + "source": "/using-unleash/troubleshooting/flag-exposure", + "destination": "troubleshooting", + "permanent": true + }, + { + "source": "/using-unleash/troubleshooting/flag-not-returned", + "destination": "troubleshooting", + "permanent": true + }, + { + "source": "/how-to/troubleshooting/https", + "destination": "/using-unleash/deploy/https", + "permanent": true + }, + { + "source": "/using-unleash/troubleshooting/feature-not-available", + "destination": "/availability", + "permanent": true + }, + { + "source": "/how-to/how-to-download-login-history", + "destination": "/reference/login-history", + "permanent": true + }, + { + "source": "how-to/how-to-manage-public-invite-tokens", + "destination": "/reference/public-signup", "permanent": true }, { @@ -781,6 +886,16 @@ "destination": "/how-to-create-feature-flag", "permanent": true }, + { + "source": "/how-to/how-to-create-and-assign-custom-root-roles", + "destination": "/reference/rbac#create-and-assign-a-custom-root-role", + "permanent": true + }, + { + "source": "/how-to/how-to-create-and-assign-custom-project-roles", + "destination": "/reference/rbac#create-and-assign-a-custom-project-role", + "permanent": true + }, { "source": "/how-to/how-to-add-strategy-constraints", "destination": "/reference/activation-strategies#add-strategy-constraint", @@ -821,6 +936,26 @@ "destination": "/using-unleash/deploy/getting-started", "permanent": true }, + { + "source": "/topics", + "destination": "/topics/what-is-a-feature-flag", + "permanent": true + }, + { + "source": "/use-cases", + "destination": "/feature-flag-tutorials/use-cases/gradual-rollout", + "permanent": true + }, + { + "source": "/languages-and-frameworks", + "destination": "/feature-flag-tutorials/react", + "permanent": true + }, + { + "source": "/feature-flag-tutorials", + "destination": "/feature-flag-tutorials/use-cases/gradual-rollout", + "permanent": true + }, { "source": "/reference/deploy/configuring-unleash", "destination": "/using-unleash/deploy/configuring-unleash", @@ -831,6 +966,26 @@ "destination": "/using-unleash/deploy/configuring-unleash", "permanent": true }, + { + "source": "/using-unleash/troubleshooting", + "destination": "/using-unleash/troubleshooting/cors", + "permanent": true + }, + { + "source": "/contributing", + "destination": "/contributing/developer-guide", + "permanent": true + }, + { + "source": "/how-to/sso", + "destination": "/how-to/how-to-add-sso-open-id-connect", + "permanent": true + }, + { + "source": "/how-to/provisioning", + "destination": "/how-to/how-to-setup-provisioning-with-okta", + "permanent": true + }, { "source": "/reference/deploy/configuring-unleash-v3", "destination": "/using-unleash/deploy/configuring-unleash-v3", @@ -908,12 +1063,12 @@ }, { "source": "/reference/deploy/import-export", - "destination": "/how-to/how-to-import-export", + "destination": "/reference/environment-import-export", "permanent": true }, { "source": "/deploy/import_export", - "destination": "/how-to/how-to-import-export", + "destination": "/reference/environment-import-export", "permanent": true }, { diff --git a/yarn.lock b/yarn.lock index 8f25efa6b0..c6efb813b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7744,9 +7744,9 @@ __metadata: languageName: node linkType: hard -"unleash-client@npm:^6.7.0-beta.1": - version: 6.7.0-beta.1 - resolution: "unleash-client@npm:6.7.0-beta.1" +"unleash-client@npm:^6.7.0-beta.3": + version: 6.7.0-beta.3 + resolution: "unleash-client@npm:6.7.0-beta.3" dependencies: http-proxy-agent: "npm:^7.0.2" https-proxy-agent: "npm:^7.0.5" @@ -7756,7 +7756,7 @@ __metadata: murmurhash3js: "npm:^3.0.1" proxy-from-env: "npm:^1.1.0" semver: "npm:^7.6.2" - checksum: 10c0/8d154eee68d9f986be4dc8b2e560eb9414db07d12c3da241b6670869abc7a67120c3ac0b1667a5557050a77cd92033c72984ae1bccfb8d692fe5739c362f5411 + checksum: 10c0/b1c28584f9e955e02495da46b8ce153d876b32b7a7ad34e9ebd38a750c1c5a804f7d0563f61e30149771dc2bf368c68a77005e9b5b1fe832fe66f560bb458b67 languageName: node linkType: hard @@ -7874,7 +7874,7 @@ __metadata: type-is: "npm:^2.0.0" typescript: "npm:5.8.3" ulidx: "npm:^2.4.1" - unleash-client: "npm:^6.7.0-beta.1" + unleash-client: "npm:^6.7.0-beta.3" uuid: "npm:^11.0.0" vite-node: "npm:^3.1.3" vitest: "npm:^3.1.3"