From 994db02f849a1c2d25edd87628027fd94a5b4e57 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Thu, 16 Dec 2021 11:07:19 +0100 Subject: [PATCH] fix: Adds feature-variant-updated event. (#1189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This triggers when we update or overwrite variants, and will include the previous variants and the new variants. Co-authored-by: Ivar Ă˜sthus --- src/lib/db/feature-toggle-store.ts | 3 +- src/lib/routes/admin-api/project/variants.ts | 17 +++++++--- src/lib/services/feature-toggle-service.ts | 33 ++++++++++++++++--- src/lib/types/events.ts | 28 +++++++++++++++- src/lib/types/stores/feature-toggle-store.ts | 1 + src/test/e2e/api/admin/feature.e2e.test.ts | 4 +++ .../fixtures/fake-feature-toggle-store.ts | 1 + 7 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/lib/db/feature-toggle-store.ts b/src/lib/db/feature-toggle-store.ts index bee2fcf52b..d8d18b5000 100644 --- a/src/lib/db/feature-toggle-store.ts +++ b/src/lib/db/feature-toggle-store.ts @@ -259,12 +259,13 @@ export default class FeatureToggleStore implements IFeatureToggleStore { } async saveVariants( + project: string, featureName: string, newVariants: IVariant[], ): Promise { const row = await this.db(TABLE) .update({ variants: JSON.stringify(newVariants) }) - .where({ name: featureName }) + .where({ project: project, name: featureName }) .returning(FEATURE_COLUMNS); return this.rowToFeature(row[0]); } diff --git a/src/lib/routes/admin-api/project/variants.ts b/src/lib/routes/admin-api/project/variants.ts index a73e076821..415731f0c5 100644 --- a/src/lib/routes/admin-api/project/variants.ts +++ b/src/lib/routes/admin-api/project/variants.ts @@ -7,6 +7,8 @@ import { Request, Response } from 'express'; import { Operation } from 'fast-json-patch'; import { UPDATE_FEATURE } from '../../../types/permissions'; import { IVariant } from '../../../types/model'; +import { extractUsername } from '../../../util/extract-user'; +import { IAuthRequest } from '../../unleash-types'; const PREFIX = '/:projectId/features/:featureName/variants'; @@ -47,13 +49,17 @@ export default class VariantsController extends Controller { } async patchVariants( - req: Request, + req: IAuthRequest, res: Response, ): Promise { - const { featureName } = req.params; + const { projectId, featureName } = req.params; + const userName = extractUsername(req); + const updatedFeature = await this.featureService.updateVariants( featureName, + projectId, req.body, + userName, ); res.status(200).json({ version: '1', @@ -62,13 +68,16 @@ export default class VariantsController extends Controller { } async overwriteVariants( - req: Request, + req: IAuthRequest, res: Response, ): Promise { - const { featureName } = req.params; + const { projectId, featureName } = req.params; + const userName = extractUsername(req); const updatedFeature = await this.featureService.saveVariants( featureName, + projectId, req.body, + userName, ); res.status(200).json({ version: '1', diff --git a/src/lib/services/feature-toggle-service.ts b/src/lib/services/feature-toggle-service.ts index 0e31a06960..eaf844c148 100644 --- a/src/lib/services/feature-toggle-service.ts +++ b/src/lib/services/feature-toggle-service.ts @@ -11,6 +11,7 @@ import { variantsArraySchema, } from '../schema/feature-schema'; import { + FEATURE_UPDATED, FeatureArchivedEvent, FeatureChangeProjectEvent, FeatureCreatedEvent, @@ -22,7 +23,7 @@ import { FeatureStrategyAddEvent, FeatureStrategyRemoveEvent, FeatureStrategyUpdateEvent, - FEATURE_UPDATED, + FeatureVariantEvent, } from '../types/events'; import NotFoundError from '../error/notfound-error'; import { @@ -36,8 +37,8 @@ import { IFeatureToggleStore } from '../types/stores/feature-toggle-store'; import { FeatureToggle, FeatureToggleDTO, - FeatureToggleWithEnvironment, FeatureToggleLegacy, + FeatureToggleWithEnvironment, IEnvironmentDetail, IFeatureEnvironmentInfo, IFeatureOverview, @@ -232,6 +233,7 @@ class FeatureToggleService { throw e; } } + /** * PUT /api/admin/projects/:projectId/features/:featureName/strategies/:strategyId ? * { @@ -910,20 +912,43 @@ class FeatureToggleService { async updateVariants( featureName: string, + project: string, newVariants: Operation[], + createdBy: string, ): Promise { const oldVariants = await this.getVariants(featureName); const { newDocument } = await applyPatch(oldVariants, newVariants); - return this.saveVariants(featureName, newDocument); + return this.saveVariants(featureName, project, newDocument, createdBy); } async saveVariants( featureName: string, + project: string, newVariants: IVariant[], + createdBy: string, ): Promise { await variantsArraySchema.validateAsync(newVariants); const fixedVariants = this.fixVariantWeights(newVariants); - return this.featureToggleStore.saveVariants(featureName, fixedVariants); + const oldVariants = await this.featureToggleStore.getVariants( + featureName, + ); + const featureToggle = await this.featureToggleStore.saveVariants( + project, + featureName, + fixedVariants, + ); + const tags = await this.tagStore.getAllTagsForFeature(featureName); + await this.eventStore.store( + new FeatureVariantEvent({ + project, + featureName, + createdBy, + tags, + oldVariants, + newVariants: featureToggle.variants, + }), + ); + return featureToggle; } fixVariantWeights(variants: IVariant[]): IVariant[] { diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index f250b9e113..04bf3edfa9 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -1,4 +1,4 @@ -import { FeatureToggle, IStrategyConfig, ITag } from './model'; +import { FeatureToggle, IStrategyConfig, ITag, IVariant } from './model'; export const APPLICATION_CREATED = 'application-created'; @@ -7,6 +7,7 @@ export const FEATURE_CREATED = 'feature-created'; export const FEATURE_DELETED = 'feature-deleted'; export const FEATURE_UPDATED = 'feature-updated'; export const FEATURE_METADATA_UPDATED = 'feature-metadata-updated'; +export const FEATURE_VARIANTS_UPDATED = 'feature-variants-updated'; export const FEATURE_PROJECT_CHANGE = 'feature-project-change'; export const FEATURE_ARCHIVED = 'feature-archived'; export const FEATURE_REVIVED = 'feature-revived'; @@ -140,6 +141,31 @@ export class FeatureEnvironmentEvent extends BaseEvent { } } +export class FeatureVariantEvent extends BaseEvent { + readonly project: string; + + readonly featureName: string; + + readonly data: { + oldVariants: IVariant[]; + newVariants: IVariant[]; + }; + + constructor(p: { + project: string; + featureName: string; + createdBy: string; + tags: ITag[]; + oldVariants: IVariant[]; + newVariants: IVariant[]; + }) { + super(FEATURE_VARIANTS_UPDATED, p.createdBy, p.tags); + this.project = p.project; + this.featureName = p.featureName; + this.data = { oldVariants: p.oldVariants, newVariants: p.newVariants }; + } +} + export class FeatureChangeProjectEvent extends BaseEvent { readonly project: string; diff --git a/src/lib/types/stores/feature-toggle-store.ts b/src/lib/types/stores/feature-toggle-store.ts index f950813fd0..77e5626607 100644 --- a/src/lib/types/stores/feature-toggle-store.ts +++ b/src/lib/types/stores/feature-toggle-store.ts @@ -18,6 +18,7 @@ export interface IFeatureToggleStore extends Store { getAll(query?: Partial): Promise; getVariants(featureName: string): Promise; saveVariants( + project: string, featureName: string, newVariants: IVariant[], ): Promise; diff --git a/src/test/e2e/api/admin/feature.e2e.test.ts b/src/test/e2e/api/admin/feature.e2e.test.ts index 287fb8d622..d509741fed 100644 --- a/src/test/e2e/api/admin/feature.e2e.test.ts +++ b/src/test/e2e/api/admin/feature.e2e.test.ts @@ -38,10 +38,14 @@ beforeAll(async () => { const createVariants = async ( featureName: string, variants: IVariant[], + projectId: string = 'default', + username: string = 'test', ) => { await app.services.featureToggleServiceV2.saveVariants( featureName, + projectId, variants, + username, ); }; diff --git a/src/test/fixtures/fake-feature-toggle-store.ts b/src/test/fixtures/fake-feature-toggle-store.ts index 143168314c..afdf5722bf 100644 --- a/src/test/fixtures/fake-feature-toggle-store.ts +++ b/src/test/fixtures/fake-feature-toggle-store.ts @@ -134,6 +134,7 @@ export default class FakeFeatureToggleStore implements IFeatureToggleStore { } async saveVariants( + project: string, featureName: string, newVariants: IVariant[], ): Promise {