mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-28 17:55:15 +02:00
feat: display created by user in search (#7292)
This commit is contained in:
parent
e79d0707cf
commit
2cc4b5faab
@ -13,8 +13,13 @@ const setupApi = () => {
|
|||||||
name: 'featureA',
|
name: 'featureA',
|
||||||
tags: [{ type: 'backend', value: 'sdk' }],
|
tags: [{ type: 'backend', value: 'sdk' }],
|
||||||
type: 'operational',
|
type: 'operational',
|
||||||
|
createdBy: { id: 1, name: 'author' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'featureB',
|
||||||
|
type: 'release',
|
||||||
|
createdBy: { id: 1, name: 'author' },
|
||||||
},
|
},
|
||||||
{ name: 'featureB', type: 'release' },
|
|
||||||
];
|
];
|
||||||
testServerRoute(server, '/api/admin/search/features', {
|
testServerRoute(server, '/api/admin/search/features', {
|
||||||
features,
|
features,
|
||||||
|
@ -39,6 +39,7 @@ import {
|
|||||||
useProjectFeatureSearch,
|
useProjectFeatureSearch,
|
||||||
useProjectFeatureSearchActions,
|
useProjectFeatureSearchActions,
|
||||||
} from './useProjectFeatureSearch';
|
} from './useProjectFeatureSearch';
|
||||||
|
import { UserAvatar } from '../../../common/UserAvatar/UserAvatar';
|
||||||
|
|
||||||
interface IPaginatedProjectFeatureTogglesProps {
|
interface IPaginatedProjectFeatureTogglesProps {
|
||||||
environments: string[];
|
environments: string[];
|
||||||
@ -101,6 +102,7 @@ export const ProjectFeatureToggles = ({
|
|||||||
const isPlaceholder = Boolean(initialLoad || (loading && total));
|
const isPlaceholder = Boolean(initialLoad || (loading && total));
|
||||||
|
|
||||||
const featureLifecycleEnabled = useUiFlag('featureLifecycle');
|
const featureLifecycleEnabled = useUiFlag('featureLifecycle');
|
||||||
|
const flagCreatorEnabled = useUiFlag('flagCreator');
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
@ -167,6 +169,30 @@ export const ProjectFeatureToggles = ({
|
|||||||
width: '1%',
|
width: '1%',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
...(flagCreatorEnabled
|
||||||
|
? [
|
||||||
|
columnHelper.accessor('createdBy', {
|
||||||
|
id: 'createdBy',
|
||||||
|
header: 'By',
|
||||||
|
cell: ({ row: { original } }) => {
|
||||||
|
return (
|
||||||
|
<UserAvatar
|
||||||
|
user={{
|
||||||
|
id: original.createdBy.id,
|
||||||
|
name: original.createdBy.name,
|
||||||
|
imageUrl: original.createdBy.imageUrl,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableSorting: false,
|
||||||
|
meta: {
|
||||||
|
width: '1%',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
columnHelper.accessor('lastSeenAt', {
|
columnHelper.accessor('lastSeenAt', {
|
||||||
id: 'lastSeenAt',
|
id: 'lastSeenAt',
|
||||||
header: 'Last seen',
|
header: 'Last seen',
|
||||||
@ -305,6 +331,11 @@ export const ProjectFeatureToggles = ({
|
|||||||
type: '-',
|
type: '-',
|
||||||
name: `Feature name ${index}`,
|
name: `Feature name ${index}`,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
|
createdBy: {
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
imageUrl: '',
|
||||||
|
},
|
||||||
dependencyType: null,
|
dependencyType: null,
|
||||||
favorite: false,
|
favorite: false,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
@ -404,6 +435,16 @@ export const ProjectFeatureToggles = ({
|
|||||||
id: 'createdAt',
|
id: 'createdAt',
|
||||||
isVisible: columnVisibility.createdAt,
|
isVisible: columnVisibility.createdAt,
|
||||||
},
|
},
|
||||||
|
...(flagCreatorEnabled
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
header: 'By',
|
||||||
|
id: 'createdBy',
|
||||||
|
isVisible:
|
||||||
|
columnVisibility.createdBy,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
header: 'Last seen',
|
header: 'Last seen',
|
||||||
id: 'lastSeenAt',
|
id: 'lastSeenAt',
|
||||||
|
@ -57,6 +57,7 @@ export const useDefaultColumnVisibility = (allColumnIds: string[]) => {
|
|||||||
'lastSeenAt',
|
'lastSeenAt',
|
||||||
...(featureLifecycleEnabled ? ['lifecycle'] : []),
|
...(featureLifecycleEnabled ? ['lifecycle'] : []),
|
||||||
'createdAt',
|
'createdAt',
|
||||||
|
'createdBy',
|
||||||
'type',
|
'type',
|
||||||
'tags',
|
'tags',
|
||||||
...showEnvironments(3),
|
...showEnvironments(3),
|
||||||
|
@ -87,6 +87,7 @@ export type UiFlags = {
|
|||||||
enableLegacyVariants?: boolean;
|
enableLegacyVariants?: boolean;
|
||||||
navigationSidebar?: boolean;
|
navigationSidebar?: boolean;
|
||||||
commandBarUI?: boolean;
|
commandBarUI?: boolean;
|
||||||
|
flagCreator?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IVersionInfo {
|
export interface IVersionInfo {
|
||||||
|
@ -28,7 +28,7 @@ export interface FeatureSearchResponseSchema {
|
|||||||
*/
|
*/
|
||||||
createdAt: string | null;
|
createdAt: string | null;
|
||||||
/** User who created the feature flag */
|
/** User who created the feature flag */
|
||||||
createdBy?: FeatureSearchResponseSchemaCreatedBy;
|
createdBy: FeatureSearchResponseSchemaCreatedBy;
|
||||||
/**
|
/**
|
||||||
* The type of dependency. 'parent' means that the feature is a parent feature, 'child' means that the feature is a child feature.
|
* The type of dependency. 'parent' means that the feature is a parent feature, 'child' means that the feature is a child feature.
|
||||||
* @nullable
|
* @nullable
|
||||||
|
@ -14,7 +14,7 @@ export type FeatureSearchResponseSchemaCreatedBy = {
|
|||||||
* URL used for the user profile image
|
* URL used for the user profile image
|
||||||
* @nullable
|
* @nullable
|
||||||
*/
|
*/
|
||||||
imageUrl: string | null;
|
imageUrl: string;
|
||||||
/** Name of the user */
|
/** Name of the user */
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
@ -122,6 +122,7 @@ exports[`should create default config 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"filterInvalidClientMetrics": false,
|
"filterInvalidClientMetrics": false,
|
||||||
|
"flagCreator": false,
|
||||||
"googleAuthEnabled": false,
|
"googleAuthEnabled": false,
|
||||||
"killInsightsUI": false,
|
"killInsightsUI": false,
|
||||||
"killScheduledChangeRequestCache": false,
|
"killScheduledChangeRequestCache": false,
|
||||||
|
@ -19,6 +19,7 @@ import type {
|
|||||||
} from '../feature-toggle/types/feature-toggle-strategies-store-type';
|
} from '../feature-toggle/types/feature-toggle-strategies-store-type';
|
||||||
import { applyGenericQueryParams, applySearchFilters } from './search-utils';
|
import { applyGenericQueryParams, applySearchFilters } from './search-utils';
|
||||||
import type { FeatureSearchEnvironmentSchema } from '../../openapi/spec/feature-search-environment-schema';
|
import type { FeatureSearchEnvironmentSchema } from '../../openapi/spec/feature-search-environment-schema';
|
||||||
|
import { generateImageUrl } from '../../util';
|
||||||
|
|
||||||
const sortEnvironments = (overview: IFeatureOverview[]) => {
|
const sortEnvironments = (overview: IFeatureOverview[]) => {
|
||||||
return overview.map((data: IFeatureOverview) => ({
|
return overview.map((data: IFeatureOverview) => ({
|
||||||
@ -404,6 +405,11 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
|||||||
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
// Create a new entry
|
// Create a new entry
|
||||||
|
const name =
|
||||||
|
row.user_name ||
|
||||||
|
row.user_username ||
|
||||||
|
row.user_email ||
|
||||||
|
'unknown';
|
||||||
entry = {
|
entry = {
|
||||||
type: row.type,
|
type: row.type,
|
||||||
description: row.description,
|
description: row.description,
|
||||||
@ -419,12 +425,12 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
|||||||
segments: row.segment_name ? [row.segment_name] : [],
|
segments: row.segment_name ? [row.segment_name] : [],
|
||||||
createdBy: {
|
createdBy: {
|
||||||
id: Number(row.user_id),
|
id: Number(row.user_id),
|
||||||
name:
|
name: name,
|
||||||
row.user_name ||
|
imageUrl: generateImageUrl({
|
||||||
row.user_username ||
|
id: row.user_id,
|
||||||
row.user_email ||
|
email: row.user_email,
|
||||||
'unknown',
|
username: name,
|
||||||
imageUrl: row.user_image_url,
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (featureLifecycleEnabled) {
|
if (featureLifecycleEnabled) {
|
||||||
|
@ -175,11 +175,21 @@ test('should search matching features by name', async () => {
|
|||||||
features: [
|
features: [
|
||||||
{
|
{
|
||||||
name: 'my_feature_a',
|
name: 'my_feature_a',
|
||||||
createdBy: { id: 1, name: 'user@getunleash.io' },
|
createdBy: {
|
||||||
|
id: 1,
|
||||||
|
name: 'user@getunleash.io',
|
||||||
|
imageUrl:
|
||||||
|
'https://gravatar.com/avatar/3957b71c0a6d2528f03b423f432ed2efe855d263400f960248a1080493d9d68a?s=42&d=retro&r=g',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'my_feature_b',
|
name: 'my_feature_b',
|
||||||
createdBy: { id: 1, name: 'user@getunleash.io' },
|
createdBy: {
|
||||||
|
id: 1,
|
||||||
|
name: 'user@getunleash.io',
|
||||||
|
imageUrl:
|
||||||
|
'https://gravatar.com/avatar/3957b71c0a6d2528f03b423f432ed2efe855d263400f960248a1080493d9d68a?s=42&d=retro&r=g',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
total: 2,
|
total: 2,
|
||||||
|
@ -21,6 +21,7 @@ export const featureSearchResponseSchema = {
|
|||||||
'favorite',
|
'favorite',
|
||||||
'impressionData',
|
'impressionData',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
|
'createdBy',
|
||||||
'environments',
|
'environments',
|
||||||
'segments',
|
'segments',
|
||||||
],
|
],
|
||||||
@ -197,7 +198,6 @@ export const featureSearchResponseSchema = {
|
|||||||
description: `URL used for the user profile image`,
|
description: `URL used for the user profile image`,
|
||||||
type: 'string',
|
type: 'string',
|
||||||
example: 'https://example.com/242x200.png',
|
example: 'https://example.com/242x200.png',
|
||||||
nullable: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -62,7 +62,8 @@ export type IFlagKey =
|
|||||||
| 'enableLegacyVariants'
|
| 'enableLegacyVariants'
|
||||||
| 'debugMetrics'
|
| 'debugMetrics'
|
||||||
| 'navigationSidebar'
|
| 'navigationSidebar'
|
||||||
| 'commandBarUI';
|
| 'commandBarUI'
|
||||||
|
| 'flagCreator';
|
||||||
|
|
||||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||||
|
|
||||||
@ -299,6 +300,10 @@ const flags: IFlags = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_COMMAND_BAR_UI,
|
process.env.UNLEASH_EXPERIMENTAL_COMMAND_BAR_UI,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
flagCreator: parseEnvVarBoolean(
|
||||||
|
process.env.UNLEASH_EXPERIMENTAL_FLAG_CREATOR,
|
||||||
|
false,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||||
|
@ -53,6 +53,7 @@ process.nextTick(async () => {
|
|||||||
createProjectWithEnvironmentConfig: true,
|
createProjectWithEnvironmentConfig: true,
|
||||||
manyStrategiesPagination: true,
|
manyStrategiesPagination: true,
|
||||||
enableLegacyVariants: false,
|
enableLegacyVariants: false,
|
||||||
|
flagCreator: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authentication: {
|
authentication: {
|
||||||
|
Loading…
Reference in New Issue
Block a user