1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-18 00:19:49 +01:00

feat: tag feature on creation (#7664)

Now it is possible to tag feature on creation.
This commit is contained in:
Jaanus Sellin 2024-07-25 13:36:28 +03:00 committed by GitHub
parent 369518cd7d
commit 1e3c690185
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 60 additions and 7 deletions

View File

@ -1256,7 +1256,7 @@ class FeatureToggleService {
await this.validateName(value.name); await this.validateName(value.name);
await this.validateFeatureFlagNameAgainstPattern(value.name, projectId); await this.validateFeatureFlagNameAgainstPattern(value.name, projectId);
const exists = await this.projectStore.hasProject(projectId); const projectExists = await this.projectStore.hasProject(projectId);
if (await this.projectStore.isFeatureLimitReached(projectId)) { if (await this.projectStore.isFeatureLimitReached(projectId)) {
throw new InvalidOperationError( throw new InvalidOperationError(
@ -1266,7 +1266,7 @@ class FeatureToggleService {
await this.validateFeatureFlagLimit(); await this.validateFeatureFlagLimit();
if (exists) { if (projectExists) {
let featureData: FeatureToggleInsert; let featureData: FeatureToggleInsert;
if (isValidated) { if (isValidated) {
featureData = { createdByUserId: auditUser.id, ...value }; featureData = { createdByUserId: auditUser.id, ...value };
@ -1301,6 +1301,16 @@ class FeatureToggleService {
); );
} }
if (value.tags && value.tags.length > 0) {
const mapTagsToFeatureTagInserts = value.tags.map((tag) => ({
tagValue: tag.value,
tagType: tag.type,
createdByUserId: auditUser.id,
featureName: featureName,
}));
await this.tagStore.tagFeatures(mapTagsToFeatureTagInserts);
}
await this.eventService.storeEvent( await this.eventService.storeEvent(
new FeatureCreatedEvent({ new FeatureCreatedEvent({
featureName, featureName,

View File

@ -8,7 +8,10 @@ test('createFeatureSchema', () => {
description: description:
'Controls disabling of the comments section in case of an incident', 'Controls disabling of the comments section in case of an incident',
impressionData: false, impressionData: false,
tags: ['simple:test', 'simple:test2'], tags: [
{ type: 'simple', value: 'tag' },
{ type: 'simple', value: 'mytag' },
],
}; };
expect( expect(

View File

@ -1,4 +1,5 @@
import type { FromSchema } from 'json-schema-to-ts'; import type { FromSchema } from 'json-schema-to-ts';
import { tagSchema } from './tag-schema';
export const createFeatureSchema = { export const createFeatureSchema = {
$id: '#/components/schemas/createFeatureSchema', $id: '#/components/schemas/createFeatureSchema',
@ -32,14 +33,17 @@ export const createFeatureSchema = {
}, },
tags: { tags: {
type: 'array', type: 'array',
description: 'Tags to add to the feature.',
items: { items: {
type: 'string', $ref: '#/components/schemas/tagSchema',
example: 'simple:test',
}, },
description: 'List of tags associated with the feature',
}, },
}, },
components: {}, components: {
schemas: {
tagSchema,
},
},
} as const; } as const;
export type CreateFeatureSchema = FromSchema<typeof createFeatureSchema>; export type CreateFeatureSchema = FromSchema<typeof createFeatureSchema>;

View File

@ -14,6 +14,7 @@ import { tagTypeSchema } from './tag-type-schema';
import { strategyVariantSchema } from './strategy-variant-schema'; import { strategyVariantSchema } from './strategy-variant-schema';
import { featureDependenciesSchema } from './feature-dependencies-schema'; import { featureDependenciesSchema } from './feature-dependencies-schema';
import { dependentFeatureSchema } from './dependent-feature-schema'; import { dependentFeatureSchema } from './dependent-feature-schema';
import { tagSchema } from './tag-schema';
export const exportResultSchema = { export const exportResultSchema = {
$id: '#/components/schemas/exportResultSchema', $id: '#/components/schemas/exportResultSchema',
@ -194,6 +195,7 @@ export const exportResultSchema = {
tagTypeSchema, tagTypeSchema,
featureDependenciesSchema, featureDependenciesSchema,
dependentFeatureSchema, dependentFeatureSchema,
tagSchema,
}, },
}, },
} as const; } as const;

View File

@ -8,6 +8,7 @@ import { featureStrategySchema } from './feature-strategy-schema';
import { environmentSchema } from './environment-schema'; import { environmentSchema } from './environment-schema';
import { featureEnvironmentSchema } from './feature-environment-schema'; import { featureEnvironmentSchema } from './feature-environment-schema';
import { strategyVariantSchema } from './strategy-variant-schema'; import { strategyVariantSchema } from './strategy-variant-schema';
import { tagSchema } from './tag-schema';
export const featuresSchema = { export const featuresSchema = {
$id: '#/components/schemas/featuresSchema', $id: '#/components/schemas/featuresSchema',
@ -40,6 +41,7 @@ export const featuresSchema = {
strategyVariantSchema, strategyVariantSchema,
parametersSchema, parametersSchema,
variantSchema, variantSchema,
tagSchema,
}, },
}, },
} as const; } as const;

View File

@ -104,6 +104,15 @@ export const featureMetadataSchema = joi
.unique((a, b) => a.name === b.name) .unique((a, b) => a.name === b.name)
.optional() .optional()
.items(variantsSchema), .items(variantsSchema),
tags: joi
.array()
.optional()
.items(
joi.object().keys({
type: joi.string().required(),
value: joi.string().required(),
}),
),
createdByUserId: joi.number(), createdByUserId: joi.number(),
}) })
.options({ allowUnknown: false, stripUnknown: true, abortEarly: false }); .options({ allowUnknown: false, stripUnknown: true, abortEarly: false });

View File

@ -63,6 +63,7 @@ export interface FeatureToggleDTO {
createdAt?: Date; createdAt?: Date;
impressionData?: boolean; impressionData?: boolean;
variants?: IVariant[]; variants?: IVariant[];
tags?: ITag[];
createdByUserId?: number; createdByUserId?: number;
createdBy?: { createdBy?: {
id: number; id: number;

View File

@ -358,3 +358,25 @@ test('returns a feature flags impression data for a different project', async ()
expect(projectFlag.impressionData).toBe(true); expect(projectFlag.impressionData).toBe(true);
}); });
}); });
test('Can add tags while creating feature flag', async () => {
const featureName = 'test.feature.with.tagss';
const tags = [{ value: 'tag1', type: 'simple' }];
await app.request.post('/api/admin/tags').send(tags[0]);
await app.request.post('/api/admin/projects/default/features').send({
name: featureName,
type: 'killswitch',
tags,
});
const { body } = await app.request
.get(`/api/admin/features/${featureName}/tags`)
.expect('Content-Type', /json/)
.expect(200);
expect(body).toMatchObject({
tags,
});
});