mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-10 17:53:36 +02:00
fix: delta do not return archived as changed (#9062)
Our delta API was returning archived feature as updated. Now making sure we do not put `archived-feature `event into `updated` event array. Also stop returning removed as complex object.
This commit is contained in:
parent
13fb7c4a63
commit
8bf1b783e9
@ -9,10 +9,8 @@ import type {
|
|||||||
import type { Logger } from '../../logger';
|
import type { Logger } from '../../logger';
|
||||||
|
|
||||||
import type { FeatureConfigurationClient } from '../feature-toggle/types/feature-toggle-strategies-store-type';
|
import type { FeatureConfigurationClient } from '../feature-toggle/types/feature-toggle-strategies-store-type';
|
||||||
import type {
|
import type { ClientFeatureToggleDelta } from './delta/client-feature-toggle-delta';
|
||||||
RevisionDeltaEntry,
|
import type { ClientFeaturesDeltaSchema } from '../../openapi';
|
||||||
ClientFeatureToggleDelta,
|
|
||||||
} from './delta/client-feature-toggle-delta';
|
|
||||||
|
|
||||||
export class ClientFeatureToggleService {
|
export class ClientFeatureToggleService {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
@ -44,7 +42,7 @@ export class ClientFeatureToggleService {
|
|||||||
async getClientDelta(
|
async getClientDelta(
|
||||||
revisionId: number | undefined,
|
revisionId: number | undefined,
|
||||||
query: IFeatureToggleQuery,
|
query: IFeatureToggleQuery,
|
||||||
): Promise<RevisionDeltaEntry | undefined> {
|
): Promise<ClientFeaturesDeltaSchema | undefined> {
|
||||||
if (this.clientFeatureToggleDelta !== null) {
|
if (this.clientFeatureToggleDelta !== null) {
|
||||||
return this.clientFeatureToggleDelta.getDelta(revisionId, query);
|
return this.clientFeatureToggleDelta.getDelta(revisionId, query);
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,3 +140,37 @@ const syncRevisions = async () => {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await app.services.clientFeatureToggleService.clientFeatureToggleDelta.onUpdateRevisionEvent();
|
await app.services.clientFeatureToggleService.clientFeatureToggleDelta.onUpdateRevisionEvent();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test('archived features should not be returned as updated', async () => {
|
||||||
|
await app.createFeature('base_feature');
|
||||||
|
await syncRevisions();
|
||||||
|
const { body } = await app.request.get('/api/client/delta').expect(200);
|
||||||
|
const currentRevisionId = body.revisionId;
|
||||||
|
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
updated: [
|
||||||
|
{
|
||||||
|
name: 'base_feature',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.archiveFeature('base_feature');
|
||||||
|
await app.createFeature('new_feature');
|
||||||
|
|
||||||
|
await syncRevisions();
|
||||||
|
|
||||||
|
const { body: deltaBody } = await app.request
|
||||||
|
.get('/api/client/delta')
|
||||||
|
.set('If-None-Match', currentRevisionId)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(deltaBody).toMatchObject({
|
||||||
|
updated: [
|
||||||
|
{
|
||||||
|
name: 'new_feature',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
removed: ['base_feature'],
|
||||||
|
});
|
||||||
|
});
|
@ -17,8 +17,10 @@ import type { OpenApiService } from '../../../services/openapi-service';
|
|||||||
import { NONE } from '../../../types/permissions';
|
import { NONE } from '../../../types/permissions';
|
||||||
import { createResponseSchema } from '../../../openapi/util/create-response-schema';
|
import { createResponseSchema } from '../../../openapi/util/create-response-schema';
|
||||||
import type { ClientFeatureToggleService } from '../client-feature-toggle-service';
|
import type { ClientFeatureToggleService } from '../client-feature-toggle-service';
|
||||||
import type { RevisionDeltaEntry } from './client-feature-toggle-delta';
|
import {
|
||||||
import { clientFeaturesDeltaSchema } from '../../../openapi';
|
type ClientFeaturesDeltaSchema,
|
||||||
|
clientFeaturesDeltaSchema,
|
||||||
|
} from '../../../openapi';
|
||||||
import type { QueryOverride } from '../client-feature-toggle.controller';
|
import type { QueryOverride } from '../client-feature-toggle.controller';
|
||||||
|
|
||||||
export default class ClientFeatureToggleDeltaController extends Controller {
|
export default class ClientFeatureToggleDeltaController extends Controller {
|
||||||
@ -75,7 +77,7 @@ export default class ClientFeatureToggleDeltaController extends Controller {
|
|||||||
|
|
||||||
async getDelta(
|
async getDelta(
|
||||||
req: IAuthRequest,
|
req: IAuthRequest,
|
||||||
res: Response<RevisionDeltaEntry>,
|
res: Response<ClientFeaturesDeltaSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('deltaApi')) {
|
if (!this.flagResolver.isEnabled('deltaApi')) {
|
||||||
throw new NotFoundError();
|
throw new NotFoundError();
|
||||||
|
@ -4,7 +4,7 @@ import type { FeatureConfigurationClient } from '../../feature-toggle/types/feat
|
|||||||
export interface FeatureConfigurationDeltaClient
|
export interface FeatureConfigurationDeltaClient
|
||||||
extends FeatureConfigurationClient {
|
extends FeatureConfigurationClient {
|
||||||
description: string;
|
description: string;
|
||||||
impressionData: false;
|
impressionData: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IClientFeatureToggleDeltaReadModel {
|
export interface IClientFeatureToggleDeltaReadModel {
|
||||||
|
@ -17,6 +17,7 @@ import type {
|
|||||||
import { CLIENT_DELTA_MEMORY } from '../../../metric-events';
|
import { CLIENT_DELTA_MEMORY } from '../../../metric-events';
|
||||||
import type EventEmitter from 'events';
|
import type EventEmitter from 'events';
|
||||||
import type { Logger } from '../../../logger';
|
import type { Logger } from '../../../logger';
|
||||||
|
import type { ClientFeaturesDeltaSchema } from '../../../openapi';
|
||||||
|
|
||||||
type DeletedFeature = {
|
type DeletedFeature = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -79,6 +80,7 @@ const filterRevisionByProject = (
|
|||||||
(feature) =>
|
(feature) =>
|
||||||
projects.includes('*') || projects.includes(feature.project),
|
projects.includes('*') || projects.includes(feature.project),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { ...revision, updated, removed };
|
return { ...revision, updated, removed };
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,7 +155,7 @@ export class ClientFeatureToggleDelta {
|
|||||||
async getDelta(
|
async getDelta(
|
||||||
sdkRevisionId: number | undefined,
|
sdkRevisionId: number | undefined,
|
||||||
query: IFeatureToggleQuery,
|
query: IFeatureToggleQuery,
|
||||||
): Promise<RevisionDeltaEntry | undefined> {
|
): Promise<ClientFeaturesDeltaSchema | undefined> {
|
||||||
const projects = query.project ? query.project : ['*'];
|
const projects = query.project ? query.project : ['*'];
|
||||||
const environment = query.environment ? query.environment : 'default';
|
const environment = query.environment ? query.environment : 'default';
|
||||||
// TODO: filter by tags, what is namePrefix? anything else?
|
// TODO: filter by tags, what is namePrefix? anything else?
|
||||||
@ -181,9 +183,10 @@ export class ClientFeatureToggleDelta {
|
|||||||
projects,
|
projects,
|
||||||
);
|
);
|
||||||
|
|
||||||
const revisionResponse = {
|
const revisionResponse: ClientFeaturesDeltaSchema = {
|
||||||
...compressedRevision,
|
...compressedRevision,
|
||||||
segments: this.segments,
|
segments: this.segments,
|
||||||
|
removed: compressedRevision.removed.map((feature) => feature.name),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Promise.resolve(revisionResponse);
|
return Promise.resolve(revisionResponse);
|
||||||
@ -197,6 +200,9 @@ export class ClientFeatureToggleDelta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used in client-feature-delta-api.e2e.test.ts, do not remove
|
||||||
|
*/
|
||||||
public resetDelta() {
|
public resetDelta() {
|
||||||
this.delta = {};
|
this.delta = {};
|
||||||
}
|
}
|
||||||
@ -217,6 +223,7 @@ export class ClientFeatureToggleDelta {
|
|||||||
...new Set(
|
...new Set(
|
||||||
changeEvents
|
changeEvents
|
||||||
.filter((event) => event.featureName)
|
.filter((event) => event.featureName)
|
||||||
|
.filter((event) => event.type !== 'feature-archived')
|
||||||
.map((event) => event.featureName!),
|
.map((event) => event.featureName!),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
27
src/lib/openapi/spec/client-features-delta-schema.test.ts
Normal file
27
src/lib/openapi/spec/client-features-delta-schema.test.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { validateSchema } from '../validate';
|
||||||
|
import type { ClientFeaturesDeltaSchema } from './client-features-delta-schema';
|
||||||
|
|
||||||
|
test('clientFeaturesDeltaSchema all fields', () => {
|
||||||
|
const data: ClientFeaturesDeltaSchema = {
|
||||||
|
revisionId: 6,
|
||||||
|
updated: [
|
||||||
|
{
|
||||||
|
impressionData: false,
|
||||||
|
enabled: false,
|
||||||
|
name: 'base_feature',
|
||||||
|
description: null,
|
||||||
|
project: 'default',
|
||||||
|
stale: false,
|
||||||
|
type: 'release',
|
||||||
|
variants: [],
|
||||||
|
strategies: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
removed: [],
|
||||||
|
segments: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
validateSchema('#/components/schemas/clientFeaturesDeltaSchema', data),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user