From 1a6197660f59ed6cd34e4fa0e899431cfd68f6e9 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 5 Jun 2024 13:54:24 +0200 Subject: [PATCH] feat: add created by in search results (#7285) --- .../feature-search/feature-search-store.ts | 21 +++++++++++++++- .../feature-search/feature.search.e2e.test.ts | 11 ++++++++- .../spec/feature-search-response-schema.ts | 24 +++++++++++++++++++ src/lib/openapi/spec/user-schema.ts | 2 +- src/lib/types/model.ts | 5 ++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/lib/features/feature-search/feature-search-store.ts b/src/lib/features/feature-search/feature-search-store.ts index 60fa1a111b..4dbfef7270 100644 --- a/src/lib/features/feature-search/feature-search-store.ts +++ b/src/lib/features/feature-search/feature-search-store.ts @@ -133,6 +133,11 @@ class FeatureSearchStore implements IFeatureSearchStore { 'ft.tag_value as tag_value', 'ft.tag_type as tag_type', 'segments.name as segment_name', + 'users.id as user_id', + 'users.name as user_name', + 'users.username as user_username', + 'users.email as user_email', + 'users.image_url as user_image_url', ] as (string | Raw | Knex.QueryBuilder)[]; const lastSeenQuery = 'last_seen_at_metrics.last_seen_at'; @@ -232,7 +237,12 @@ class FeatureSearchStore implements IFeatureSearchStore { '=', 'features.name', ); - }); + }) + .leftJoin( + 'users', + 'users.id', + 'features.created_by_user_id', + ); query.leftJoin('last_seen_at_metrics', function () { this.on( @@ -407,6 +417,15 @@ class FeatureSearchStore implements IFeatureSearchStore { dependencyType: row.dependency, environments: [], segments: row.segment_name ? [row.segment_name] : [], + createdBy: { + id: Number(row.user_id), + name: + row.user_name || + row.user_username || + row.user_email || + 'unknown', + imageUrl: row.user_image_url, + }, }; if (featureLifecycleEnabled) { entry.lifecycle = row.latest_stage diff --git a/src/lib/features/feature-search/feature.search.e2e.test.ts b/src/lib/features/feature-search/feature.search.e2e.test.ts index 6d5c0af4cf..18402b79d8 100644 --- a/src/lib/features/feature-search/feature.search.e2e.test.ts +++ b/src/lib/features/feature-search/feature.search.e2e.test.ts @@ -172,7 +172,16 @@ test('should search matching features by name', async () => { const { body } = await searchFeatures({ query: 'feature' }); expect(body).toMatchObject({ - features: [{ name: 'my_feature_a' }, { name: 'my_feature_b' }], + features: [ + { + name: 'my_feature_a', + createdBy: { id: 1, name: 'user@getunleash.io' }, + }, + { + name: 'my_feature_b', + createdBy: { id: 1, name: 'user@getunleash.io' }, + }, + ], total: 2, }); }); diff --git a/src/lib/openapi/spec/feature-search-response-schema.ts b/src/lib/openapi/spec/feature-search-response-schema.ts index 3c6e746943..0c7a31b36e 100644 --- a/src/lib/openapi/spec/feature-search-response-schema.ts +++ b/src/lib/openapi/spec/feature-search-response-schema.ts @@ -177,6 +177,30 @@ export const featureSearchResponseSchema = { }, }, }, + createdBy: { + type: 'object', + description: 'User who created the feature flag', + additionalProperties: false, + required: ['id', 'name', 'imageUrl'], + properties: { + id: { + description: 'The user id', + type: 'integer', + example: 123, + }, + name: { + description: 'Name of the user', + type: 'string', + example: 'User', + }, + imageUrl: { + description: `URL used for the user profile image`, + type: 'string', + example: 'https://example.com/242x200.png', + nullable: true, + }, + }, + }, }, components: { schemas: { diff --git a/src/lib/openapi/spec/user-schema.ts b/src/lib/openapi/spec/user-schema.ts index 2082ee0f00..dff7f74afc 100644 --- a/src/lib/openapi/spec/user-schema.ts +++ b/src/lib/openapi/spec/user-schema.ts @@ -38,7 +38,7 @@ export const userSchema = { nullable: true, }, imageUrl: { - description: `URL used for the userprofile image`, + description: `URL used for the user profile image`, type: 'string', example: 'https://example.com/242x200.png', }, diff --git a/src/lib/types/model.ts b/src/lib/types/model.ts index 20461b1fd5..ba98de256c 100644 --- a/src/lib/types/model.ts +++ b/src/lib/types/model.ts @@ -247,6 +247,11 @@ export type IFeatureSearchOverview = Exclude< > & { dependencyType: 'parent' | 'child' | null; environments: FeatureSearchEnvironmentSchema[]; + createdBy: { + id: number; + name: string; + imageUrl: string | null; + }; }; export interface IFeatureTypeCount {