1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

Feat/add strategy update event on strategy ordering (#4234)

Adds a `feature-strategy-update-event` when the strategy sort-order is
changed.

Makes all fields in the eventDataSchema nullable

Closes #
[1-11120](https://linear.app/unleash/issue/1-1112/we-should-have-event-for-re-ordering-strategies)

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
andreas-unleash 2023-07-14 04:46:13 +03:00 committed by GitHub
parent 3e98e1743f
commit 16e3799b9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 155 additions and 21 deletions

View File

@ -12,29 +12,35 @@ const eventDataSchema = {
description:
'Name of the feature toggle/strategy/environment that this event relates to',
example: 'my.first.toggle',
nullable: true,
},
description: {
type: 'string',
description: 'The description of the object this event relates to',
example: 'Toggle description',
nullable: true,
},
type: {
type: 'string',
nullable: true,
description:
'If this event relates to a feature toggle, the type of feature toggle.',
example: 'release',
},
project: {
nullable: true,
type: 'string',
description: 'The project this event relates to',
example: 'default',
},
stale: {
nullable: true,
description: 'Is the feature toggle this event relates to stale',
type: 'boolean',
example: true,
},
variants: {
nullable: true,
description: 'Variants configured for this toggle',
type: 'array',
items: {
@ -42,6 +48,7 @@ const eventDataSchema = {
},
},
createdAt: {
nullable: true,
type: 'string',
format: 'date-time',
description:
@ -49,11 +56,11 @@ const eventDataSchema = {
example: '2023-07-05T12:56:00.000Z',
},
lastSeenAt: {
nullable: true,
type: 'string',
format: 'date-time',
description: 'The time the feature was last seen',
example: '2023-07-05T12:56:00.000Z',
nullable: true,
},
impressionData: {
description:
@ -127,7 +134,12 @@ export const eventSchema = {
'The name of the feature toggle the event relates to, if applicable.',
example: 'my.first.feature',
},
data: eventDataSchema,
data: {
...eventDataSchema,
description:
"Data relating to the current state of the event's subject.",
nullable: true,
},
preData: {
...eventDataSchema,
description:

View File

@ -936,7 +936,7 @@ export default class ProjectFeaturesController extends Controller {
}
async setStrategiesSortOrder(
req: Request<
req: IAuthRequest<
FeatureStrategyParams,
any,
SetStrategySortOrderSchema,
@ -944,10 +944,18 @@ export default class ProjectFeaturesController extends Controller {
>,
res: Response,
): Promise<void> {
const { featureName } = req.params;
await this.featureService.updateStrategiesSortOrder(
featureName,
req.body,
const { featureName, projectId: project, environment } = req.params;
const createdBy = extractUsername(req);
await this.startTransaction(async (tx) =>
this.transactionalFeatureToggleService(
tx,
).updateStrategiesSortOrder(
featureName,
environment,
project,
createdBy,
req.body,
),
);
res.status(200).send();

View File

@ -369,7 +369,7 @@ class FeatureToggleService {
featureStrategy: IFeatureStrategy,
segments: ISegment[] = [],
): Saved<IStrategyConfig> {
return {
const result: Saved<IStrategyConfig> = {
id: featureStrategy.id,
name: featureStrategy.strategyName,
title: featureStrategy.title,
@ -378,16 +378,56 @@ class FeatureToggleService {
parameters: featureStrategy.parameters,
segments: segments.map((segment) => segment.id) ?? [],
};
if (this.flagResolver.isEnabled('strategyVariant')) {
result.sortOrder = featureStrategy.sortOrder;
}
return result;
}
async updateStrategiesSortOrder(
featureName: string,
environment: string,
project: string,
createdBy: string,
sortOrders: SetStrategySortOrderSchema,
): Promise<Saved<any>> {
await Promise.all(
sortOrders.map(async ({ id, sortOrder }) =>
this.featureStrategiesStore.updateSortOrder(id, sortOrder),
),
sortOrders.map(async ({ id, sortOrder }) => {
const strategyToUpdate =
await this.featureStrategiesStore.getStrategyById(id);
await this.featureStrategiesStore.updateSortOrder(
id,
sortOrder,
);
const updatedStrategy =
await this.featureStrategiesStore.getStrategyById(id);
const tags = await this.tagStore.getAllTagsForFeature(
featureName,
);
const segments = await this.segmentService.getByStrategy(
strategyToUpdate.id,
);
const strategy = this.featureStrategyToPublic(
updatedStrategy,
segments,
);
await this.eventStore.store(
new FeatureStrategyUpdateEvent({
featureName,
environment,
project,
createdBy,
preData: this.featureStrategyToPublic(
strategyToUpdate,
segments,
),
data: strategy,
tags: tags,
}),
);
}),
);
}
@ -472,24 +512,31 @@ class FeatureToggleService {
);
}
const tags = await this.tagStore.getAllTagsForFeature(featureName);
const segments = await this.segmentService.getByStrategy(
newFeatureStrategy.id,
);
const strategy = this.featureStrategyToPublic(
newFeatureStrategy,
segments,
);
await this.eventStore.store(
new FeatureStrategyAddEvent({
project: projectId,
if (this.flagResolver.isEnabled('strategyVariant')) {
const tags = await this.tagStore.getAllTagsForFeature(
featureName,
createdBy,
environment,
data: strategy,
tags,
}),
);
);
await this.eventStore.store(
new FeatureStrategyAddEvent({
project: projectId,
featureName,
createdBy,
environment,
data: strategy,
tags,
}),
);
}
return strategy;
} catch (e) {
if (e.code === FOREIGN_KEY_VIOLATION) {

View File

@ -26,6 +26,10 @@ import { v4 as uuidv4 } from 'uuid';
import supertest from 'supertest';
import { randomId } from '../../../../../lib/util/random-id';
import { DEFAULT_PROJECT } from '../../../../../lib/types';
import {
FeatureStrategySchema,
SetStrategySortOrderSchema,
} from '../../../../../lib/openapi';
let app: IUnleashTest;
let db: ITestDb;
@ -3227,3 +3231,66 @@ test('Enabling a feature environment should add the default strategy when only d
expect(res.body.strategies[1].disabled).toBeFalsy();
});
});
test('Updating feature strategy sort-order should trigger a FeatureStrategyUpdatedEvent when strategyVariant is true', async () => {
app = await setupAppWithCustomConfig(
db.stores,
{
experimental: {
flags: {
strictSchemaValidation: true,
strategyVariant: true,
},
},
},
db.rawDatabase,
);
const envName = 'sort-order-within-environment-strategyVariant';
const featureName = 'feature.sort.order.event.list';
await db.stores.environmentStore.create({
name: envName,
type: 'test',
});
await app.request
.post('/api/admin/projects/default/environments')
.send({
environment: envName,
})
.expect(200);
await app.request
.post('/api/admin/projects/default/features')
.send({ name: featureName })
.expect(201);
await addStrategies(featureName, envName);
const { body } = await app.request.get(
`/api/admin/projects/default/features/${featureName}/environments/${envName}/strategies`,
);
const strategies: FeatureStrategySchema[] = body;
let order = 1;
const sortOrders: SetStrategySortOrderSchema = [];
strategies.forEach((strategy) => {
sortOrders.push({ id: strategy.id!, sortOrder: order++ });
});
await app.request
.post(
`/api/admin/projects/default/features/${featureName}/environments/${envName}/strategies/set-sort-order`,
)
.send(sortOrders)
.expect(200);
const response = await app.request.get(`/api/admin/events`);
const { body: eventsBody } = response;
let { events } = eventsBody;
expect(events[0].type).toBe('feature-strategy-update');
expect(events[1].type).toBe('feature-strategy-update');
expect(events[2].type).toBe('feature-strategy-update');
});