Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Diagnostics): display db feature flags #1229

Merged
merged 15 commits into from
Sep 9, 2024
8 changes: 8 additions & 0 deletions src/containers/Tenant/Diagnostics/Configs/Configs.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.ydb-diagnostics-configs {
&__icon-touched {
line-height: 1;
cursor: default !important;

color: var(--g-color-text-secondary);
}
}
125 changes: 125 additions & 0 deletions src/containers/Tenant/Diagnostics/Configs/Configs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {PersonPencil} from '@gravity-ui/icons';
import type {Column} from '@gravity-ui/react-data-table';
import {Icon, Popover, Switch} from '@gravity-ui/uikit';
import {StringParam, useQueryParam} from 'use-query-params';

import {ResponseError} from '../../../../components/Errors/ResponseError';
import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {Search} from '../../../../components/Search';
import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout';
import {tenantApi} from '../../../../store/reducers/tenant/tenant';
import type {FeatureFlagConfig} from '../../../../types/api/featureFlags';
import {cn} from '../../../../utils/cn';
import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';

import i18n from './i18n';

import './Configs.scss';

const FEATURE_FLAGS_COLUMNS_WIDTH_LS_KEY = 'featureFlagsColumnsWidth';

const b = cn('ydb-diagnostics-configs');

const columns: Column<FeatureFlagConfig>[] = [
{
name: 'Touched',
header: '',
render: ({row}) =>
row.Current ? (
<Popover
content={i18n('flag-touched')}
className={b('icon-touched')}
placement="left"
>
<Icon data={PersonPencil} />
</Popover>
) : null,
width: 36,
sortable: false,
resizeable: false,
},
{
name: 'Name',
get header() {
return i18n('td-feature-flag');
},
render: ({row}) => (row.Current ? <b>{row.Name}</b> : row.Name),
width: 400,
sortable: true,
sortAccessor: ({Current, Name}) => {
return Number(!Current) + Name.toLowerCase();
},
},
{
name: 'Default',
get header() {
return i18n('td-default');
},
render: ({row}) => {
switch (row.Default) {
case true:
return i18n('enabled');
case false:
return i18n('disabled');
default:
return '-';
}
},
width: 100,
sortable: false,
resizeable: false,
},
{
name: 'Current',
get header() {
return i18n('td-current');
},
render: ({row}) => <Switch disabled checked={(row.Current ?? row.Default) || false} />,
width: 100,
sortable: false,
resizeable: false,
},
];

interface ConfigsProps {
database: string;
}

export const Configs = ({database}: ConfigsProps) => {
const [search, setSearch] = useQueryParam('search', StringParam);
const {currentData = [], isFetching, error} = tenantApi.useGetClusterConfigQuery({database});

const onChange = (value: string) => {
setSearch(value || undefined, 'replaceIn');
};

const featureFlagsFilter = search?.toLocaleLowerCase();
const featureFlags = featureFlagsFilter
? currentData.filter((item) => item.Name.toLocaleLowerCase().includes(featureFlagsFilter))
: currentData;

return (
<TableWithControlsLayout>
<TableWithControlsLayout.Controls>
<Search
value={featureFlagsFilter}
onChange={onChange}
placeholder={i18n('search-placeholder')}
/>
</TableWithControlsLayout.Controls>
<TableWithControlsLayout.Table loading={isFetching}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this and fetching and filtering data to the FeatureFlagsList.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Now Configs component includes logic and FeatureFlagsList is stupid (and is not very necessary imho). After moving logic Config will be almost redundant

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed in pm, fixed

{error ? (
<ResponseError error={error} />
) : (
<ResizeableDataTable
emptyDataMessage={i18n(featureFlagsFilter ? 'search-empty' : 'no-data')}
columnsWidthLSKey={FEATURE_FLAGS_COLUMNS_WIDTH_LS_KEY}
columns={columns}
data={featureFlags}
settings={DEFAULT_TABLE_SETTINGS}
/>
)}
</TableWithControlsLayout.Table>
</TableWithControlsLayout>
);
};
13 changes: 13 additions & 0 deletions src/containers/Tenant/Diagnostics/Configs/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"td-feature-flag": "Feature flag",
"td-default": "Default",
"td-current": "Current",

"enabled": "Enabled",
"disabled": "Disabled",
"flag-touched": "Flag is changed",

"search-placeholder": "Search by feature flag",
"search-empty": "Empty search result",
"no-data": "No data"
}
7 changes: 7 additions & 0 deletions src/containers/Tenant/Diagnostics/Configs/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {registerKeysets} from '../../../../../utils/i18n';

import en from './en.json';

const COMPONENT = 'ydb-diagnostics-configs';

export default registerKeysets(COMPONENT, {en});
4 changes: 4 additions & 0 deletions src/containers/Tenant/Diagnostics/Diagnostics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer';
import {TenantTabsGroups} from '../TenantPages';
import {isDatabaseEntityType} from '../utils/schema';

import {Configs} from './Configs/Configs';
import {Consumers} from './Consumers';
import Describe from './Describe/Describe';
import DetailedOverview from './DetailedOverview/DetailedOverview';
Expand Down Expand Up @@ -131,6 +132,9 @@ function Diagnostics(props: DiagnosticsProps) {
case TENANT_DIAGNOSTICS_TABS_IDS.partitions: {
return <Partitions path={path} database={tenantName} />;
}
case TENANT_DIAGNOSTICS_TABS_IDS.configs: {
return <Configs database={tenantName} />;
}
default: {
return <div>No data...</div>;
}
Expand Down
6 changes: 6 additions & 0 deletions src/containers/Tenant/Diagnostics/DiagnosticsPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ const partitions = {
title: 'Partitions',
};

const configs = {
id: TENANT_DIAGNOSTICS_TABS_IDS.configs,
title: 'Configs',
};

export const ASYNC_REPLICATION_PAGES = [overview, tablets, describe];

export const DATABASE_PAGES = [
Expand All @@ -81,6 +86,7 @@ export const DATABASE_PAGES = [
storage,
network,
describe,
configs,
];

export const TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, hotKeys, describe];
Expand Down
10 changes: 10 additions & 0 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {CapabilitiesResponse} from '../types/api/capabilities';
import type {TClusterInfo} from '../types/api/cluster';
import type {TComputeInfo} from '../types/api/compute';
import type {DescribeConsumerResult} from '../types/api/consumer';
import type {FeatureFlagConfigs} from '../types/api/featureFlags';
import type {HealthCheckAPIResponse} from '../types/api/healthcheck';
import type {JsonHotKeysResponse} from '../types/api/hotkeys';
import type {
Expand Down Expand Up @@ -147,6 +148,15 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
{concurrentId: concurrentId || `getClusterInfo`, requestConfig: {signal}},
);
}
getClusterConfig(database?: string, {concurrentId, signal}: AxiosOptions = {}) {
return this.get<FeatureFlagConfigs>(
this.getPath('/viewer/feature_flags'),
{
database,
},
{concurrentId, requestConfig: {signal}},
);
}
getNodeInfo(id?: string | number, {concurrentId, signal}: AxiosOptions = {}) {
return this.get<TEvSystemStateResponse>(
this.getPath('/viewer/json/sysinfo?enums=true'),
Expand Down
1 change: 1 addition & 0 deletions src/store/reducers/tenant/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const TENANT_DIAGNOSTICS_TABS_IDS = {
graph: 'graph',
consumers: 'consumers',
partitions: 'partitions',
configs: 'configs',
} as const;

export const TENANT_SUMMARY_TABS_IDS = {
Expand Down
12 changes: 12 additions & 0 deletions src/store/reducers/tenant/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ export const tenantApi = api.injectEndpoints({
},
providesTags: ['All'],
}),
getClusterConfig: builder.query({
queryFn: async ({database}: {database: string}, {signal}) => {
try {
const res = await window.api.getClusterConfig(database, {signal});
const db = res.Databases[0];

return {data: db.FeatureFlags};
} catch (error) {
return {error};
}
},
}),
}),
overrideExisting: 'throw',
});
14 changes: 14 additions & 0 deletions src/types/api/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface FeatureFlagConfig {
Name: string;
Current?: boolean;
Default?: boolean;
}

interface ConfigDb {
Name: string;
FeatureFlags: FeatureFlagConfig[];
}

export interface FeatureFlagConfigs {
Databases: ConfigDb[];
}
Loading