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:
parent
3e98e1743f
commit
16e3799b9a
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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');
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user