mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-21 13:47:39 +02:00
feat: backend for retrieving tag colors (#9610)
Add backend for retrieving tag colors
This commit is contained in:
parent
9106fbf721
commit
7d7a949093
@ -102,11 +102,18 @@ class FeatureTagStore implements IFeatureTagStore {
|
||||
const stopTimer = this.timer('getAllForFeature');
|
||||
if (await this.featureExists(featureName)) {
|
||||
const rows = await this.db
|
||||
.select(COLUMNS)
|
||||
.select([...COLUMNS, 'tag_types.color as color'])
|
||||
.from<FeatureTagTable>(TABLE)
|
||||
.leftJoin('tag_types', 'tag_types.name', 'feature_tag.tag_type')
|
||||
.where({ feature_name: featureName });
|
||||
|
||||
stopTimer();
|
||||
return rows.map(this.featureTagRowToTag);
|
||||
|
||||
return rows.map((row) => ({
|
||||
type: row.tag_type,
|
||||
value: row.tag_value,
|
||||
color: row.color,
|
||||
}));
|
||||
} else {
|
||||
throw new NotFoundError(
|
||||
`Could not find feature with name ${featureName}`,
|
||||
|
@ -134,6 +134,7 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
||||
'environments.sort_order as environment_sort_order',
|
||||
'ft.tag_value as tag_value',
|
||||
'ft.tag_type as tag_type',
|
||||
'tag_types.color as tag_type_color',
|
||||
'segments.name as segment_name',
|
||||
'users.id as user_id',
|
||||
'users.name as user_name',
|
||||
@ -207,6 +208,7 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
||||
'ft.feature_name',
|
||||
'features.name',
|
||||
)
|
||||
.leftJoin('tag_types', 'tag_types.name', 'ft.tag_type')
|
||||
.leftJoin(
|
||||
'feature_strategies',
|
||||
'feature_strategies.feature_name',
|
||||
@ -548,6 +550,7 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
||||
return {
|
||||
value: r.tag_value,
|
||||
type: r.tag_type,
|
||||
color: r.tag_type_color,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1252,3 +1252,37 @@ test('should return archived when query param set', async () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('should return tags with color information from tag type', async () => {
|
||||
await app.createFeature('my_feature_a');
|
||||
|
||||
await app.request
|
||||
.put('/api/admin/tag-types/simple')
|
||||
.send({
|
||||
name: 'simple',
|
||||
color: '#FF0000',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await app.addTag('my_feature_a', {
|
||||
type: 'simple',
|
||||
value: 'my_tag',
|
||||
});
|
||||
|
||||
const { body } = await searchFeatures({});
|
||||
|
||||
expect(body).toMatchObject({
|
||||
features: [
|
||||
{
|
||||
name: 'my_feature_a',
|
||||
tags: [
|
||||
{
|
||||
type: 'simple',
|
||||
value: 'my_tag',
|
||||
color: '#FF0000',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
@ -13,6 +13,7 @@ import { projectEnvironmentSchema } from './project-environment-schema';
|
||||
import { createStrategyVariantSchema } from './create-strategy-variant-schema';
|
||||
import { strategyVariantSchema } from './strategy-variant-schema';
|
||||
import { createFeatureNamingPatternSchema } from './create-feature-naming-pattern-schema';
|
||||
import { tagSchema } from './tag-schema';
|
||||
|
||||
export const deprecatedProjectOverviewSchema = {
|
||||
$id: '#/components/schemas/deprecatedProjectOverviewSchema',
|
||||
@ -144,6 +145,7 @@ export const deprecatedProjectOverviewSchema = {
|
||||
variantSchema,
|
||||
projectStatsSchema,
|
||||
createFeatureNamingPatternSchema,
|
||||
tagSchema,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
@ -13,6 +13,7 @@ import { projectEnvironmentSchema } from './project-environment-schema';
|
||||
import { createStrategyVariantSchema } from './create-strategy-variant-schema';
|
||||
import { strategyVariantSchema } from './strategy-variant-schema';
|
||||
import { createFeatureNamingPatternSchema } from './create-feature-naming-pattern-schema';
|
||||
import { tagSchema } from './tag-schema';
|
||||
|
||||
export const healthOverviewSchema = {
|
||||
$id: '#/components/schemas/healthOverviewSchema',
|
||||
@ -138,6 +139,7 @@ export const healthOverviewSchema = {
|
||||
variantSchema,
|
||||
projectStatsSchema,
|
||||
createFeatureNamingPatternSchema,
|
||||
tagSchema,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
@ -24,6 +24,13 @@ export const tagSchema = {
|
||||
'The [type](https://docs.getunleash.io/reference/feature-toggles#tags) of the tag',
|
||||
example: 'simple',
|
||||
},
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'The hexadecimal color code for the tag type.',
|
||||
example: '#FFFFFF',
|
||||
pattern: '^#[0-9A-Fa-f]{6}$',
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
} as const;
|
||||
|
@ -355,6 +355,7 @@ export interface IFeatureToggleDeltaQuery extends IFeatureToggleQuery {
|
||||
export interface ITag {
|
||||
value: string;
|
||||
type: string;
|
||||
color?: string | null;
|
||||
}
|
||||
|
||||
export interface IAddonParameterDefinition {
|
||||
|
@ -10,13 +10,17 @@ let db: ITestDb;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('tag_api_serial', getLogger);
|
||||
app = await setupAppWithCustomConfig(db.stores, {
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
db.rawDatabase,
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@ -219,3 +223,47 @@ test('backward compatibility: the API should return invalid tag names if they ex
|
||||
const { body } = await app.request.get('/api/admin/tags').expect(200);
|
||||
expect(body.tags).toContainEqual(tag);
|
||||
});
|
||||
|
||||
test('should include tag color information when getting feature tags', async () => {
|
||||
const featureName = 'test.feature.with.color';
|
||||
const tagType = 'simple';
|
||||
const tag = {
|
||||
value: 'TeamRed',
|
||||
type: tagType,
|
||||
};
|
||||
|
||||
await app.request.post('/api/admin/projects/default/features').send({
|
||||
name: featureName,
|
||||
type: 'kill-switch',
|
||||
enabled: true,
|
||||
strategies: [{ name: 'default' }],
|
||||
});
|
||||
|
||||
await app.request
|
||||
.put(`/api/admin/tag-types/${tagType}`)
|
||||
.send({
|
||||
name: tagType,
|
||||
color: '#FF0000',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await app.request
|
||||
.put(`/api/admin/features/${featureName}/tags`)
|
||||
.send({ addedTags: [tag], removedTags: [] })
|
||||
.expect(200);
|
||||
|
||||
const { body } = await app.request
|
||||
.get(`/api/admin/features/${featureName}/tags`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
tags: [
|
||||
{
|
||||
value: 'TeamRed',
|
||||
type: 'simple',
|
||||
color: '#FF0000',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
@ -13,6 +13,7 @@ let featureToggleStore: IFeatureToggleStore;
|
||||
const featureName = 'test-tag';
|
||||
const tag = { type: 'simple', value: 'test' };
|
||||
const TESTUSERID = 3333;
|
||||
const DEFAULT_TAG_COLOR = '#FFFFFF';
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_tag_store_serial', getLogger);
|
||||
@ -45,7 +46,7 @@ test('should tag feature', async () => {
|
||||
createdByUserId: TESTUSERID,
|
||||
});
|
||||
expect(featureTags).toHaveLength(1);
|
||||
expect(featureTags[0]).toStrictEqual(tag);
|
||||
expect(featureTags[0]).toStrictEqual({ ...tag, color: DEFAULT_TAG_COLOR });
|
||||
expect(featureTag!.featureName).toBe(featureName);
|
||||
expect(featureTag!.tagValue).toBe(tag.value);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user