mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: Adds feature-variant-updated event. (#1189)
This triggers when we update or overwrite variants, and will include the previous variants and the new variants. Co-authored-by: Ivar Østhus <ivarconr@gmail.com>
This commit is contained in:
		
							parent
							
								
									b05216bc76
								
							
						
					
					
						commit
						994db02f84
					
				@ -259,12 +259,13 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async saveVariants(
 | 
			
		||||
        project: string,
 | 
			
		||||
        featureName: string,
 | 
			
		||||
        newVariants: IVariant[],
 | 
			
		||||
    ): Promise<FeatureToggle> {
 | 
			
		||||
        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]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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<FeatureParams, any, Operation[], any>,
 | 
			
		||||
        req: IAuthRequest<FeatureParams, any, Operation[]>,
 | 
			
		||||
        res: Response,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        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<FeatureParams, any, IVariant[], any>,
 | 
			
		||||
        req: IAuthRequest<FeatureParams, any, IVariant[], any>,
 | 
			
		||||
        res: Response,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        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',
 | 
			
		||||
 | 
			
		||||
@ -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<FeatureToggle> {
 | 
			
		||||
        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<FeatureToggle> {
 | 
			
		||||
        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[] {
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ export interface IFeatureToggleStore extends Store<FeatureToggle, string> {
 | 
			
		||||
    getAll(query?: Partial<IFeatureToggleQuery>): Promise<FeatureToggle[]>;
 | 
			
		||||
    getVariants(featureName: string): Promise<IVariant[]>;
 | 
			
		||||
    saveVariants(
 | 
			
		||||
        project: string,
 | 
			
		||||
        featureName: string,
 | 
			
		||||
        newVariants: IVariant[],
 | 
			
		||||
    ): Promise<FeatureToggle>;
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -134,6 +134,7 @@ export default class FakeFeatureToggleStore implements IFeatureToggleStore {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async saveVariants(
 | 
			
		||||
        project: string,
 | 
			
		||||
        featureName: string,
 | 
			
		||||
        newVariants: IVariant[],
 | 
			
		||||
    ): Promise<FeatureToggle> {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user