mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: delta api e2e test (#9003)
WIP PR for delta-api tests. --------- Co-authored-by: sjaanus <sellinjaanus@gmail.com>
This commit is contained in:
parent
563352909a
commit
71eb6b1511
@ -73,6 +73,47 @@ export default class ClientFeatureToggleDeltaController extends Controller {
|
||||
});
|
||||
}
|
||||
|
||||
async getDelta(
|
||||
req: IAuthRequest,
|
||||
res: Response<RevisionDeltaEntry>,
|
||||
): Promise<void> {
|
||||
if (!this.flagResolver.isEnabled('deltaApi')) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
const query = await this.resolveQuery(req);
|
||||
const etag = req.headers['if-none-match'];
|
||||
|
||||
const currentSdkRevisionId = etag ? Number.parseInt(etag) : undefined;
|
||||
|
||||
const changedFeatures =
|
||||
await this.clientFeatureToggleService.getClientDelta(
|
||||
currentSdkRevisionId,
|
||||
query,
|
||||
);
|
||||
|
||||
if (!changedFeatures) {
|
||||
res.status(304);
|
||||
res.getHeaderNames().forEach((header) => res.removeHeader(header));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (changedFeatures.revisionId === currentSdkRevisionId) {
|
||||
res.status(304);
|
||||
res.getHeaderNames().forEach((header) => res.removeHeader(header));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
res.setHeader('ETag', changedFeatures.revisionId.toString());
|
||||
this.openApiService.respondWithValidation(
|
||||
200,
|
||||
res,
|
||||
clientFeaturesDeltaSchema.$id,
|
||||
changedFeatures,
|
||||
);
|
||||
}
|
||||
|
||||
private async resolveQuery(
|
||||
req: IAuthRequest,
|
||||
): Promise<IFeatureToggleQuery> {
|
||||
@ -139,45 +180,4 @@ export default class ClientFeatureToggleDeltaController extends Controller {
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
async getDelta(
|
||||
req: IAuthRequest,
|
||||
res: Response<RevisionDeltaEntry>,
|
||||
): Promise<void> {
|
||||
if (!this.flagResolver.isEnabled('deltaApi')) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
const query = await this.resolveQuery(req);
|
||||
const etag = req.headers['if-none-match'];
|
||||
|
||||
const currentSdkRevisionId = etag ? Number.parseInt(etag) : undefined;
|
||||
|
||||
const changedFeatures =
|
||||
await this.clientFeatureToggleService.getClientDelta(
|
||||
currentSdkRevisionId,
|
||||
query,
|
||||
);
|
||||
|
||||
if (!changedFeatures) {
|
||||
res.status(304);
|
||||
res.getHeaderNames().forEach((header) => res.removeHeader(header));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (changedFeatures.revisionId === currentSdkRevisionId) {
|
||||
res.status(304);
|
||||
res.getHeaderNames().forEach((header) => res.removeHeader(header));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
res.setHeader('ETag', changedFeatures.revisionId.toString());
|
||||
this.openApiService.respondWithValidation(
|
||||
200,
|
||||
res,
|
||||
clientFeaturesDeltaSchema.$id,
|
||||
changedFeatures,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -169,28 +169,6 @@ export class ClientFeatureToggleDelta {
|
||||
await this.updateSegments();
|
||||
}
|
||||
|
||||
// TODO: 19.12 this logic seems to be not logical, when no revisionId is coming, it should not go to db, but take latest from cache
|
||||
|
||||
// Should get the latest state if revision does not exist or if sdkRevision is not present
|
||||
// We should be able to do this without going to the database by merging revisions from the delta with
|
||||
// the base case
|
||||
const firstTimeCalling = !sdkRevisionId;
|
||||
if (
|
||||
firstTimeCalling ||
|
||||
(sdkRevisionId &&
|
||||
sdkRevisionId !== this.currentRevisionId &&
|
||||
!this.delta[environment].hasRevision(sdkRevisionId))
|
||||
) {
|
||||
//TODO: populate delta based on this?
|
||||
return {
|
||||
revisionId: this.currentRevisionId,
|
||||
// @ts-ignore
|
||||
updated: await this.getClientFeatures({ environment }),
|
||||
segments: this.segments,
|
||||
removed: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (requiredRevisionId >= this.currentRevisionId) {
|
||||
return undefined;
|
||||
}
|
||||
@ -211,7 +189,7 @@ export class ClientFeatureToggleDelta {
|
||||
return Promise.resolve(revisionResponse);
|
||||
}
|
||||
|
||||
private async onUpdateRevisionEvent() {
|
||||
public async onUpdateRevisionEvent() {
|
||||
if (this.flagResolver.isEnabled('deltaApi')) {
|
||||
await this.updateFeaturesDelta();
|
||||
await this.updateSegments();
|
||||
@ -219,7 +197,11 @@ export class ClientFeatureToggleDelta {
|
||||
}
|
||||
}
|
||||
|
||||
public async updateFeaturesDelta() {
|
||||
public resetDelta() {
|
||||
this.delta = {};
|
||||
}
|
||||
|
||||
private async updateFeaturesDelta() {
|
||||
const keys = Object.keys(this.delta);
|
||||
|
||||
if (keys.length === 0) return;
|
||||
|
@ -0,0 +1,142 @@
|
||||
import dbInit, {
|
||||
type ITestDb,
|
||||
} from '../../../../test/e2e/helpers/database-init';
|
||||
import {
|
||||
type IUnleashTest,
|
||||
setupAppWithCustomConfig,
|
||||
} from '../../../../test/e2e/helpers/test-helper';
|
||||
import getLogger from '../../../../test/fixtures/no-logger';
|
||||
import { DEFAULT_ENV } from '../../../util/constants';
|
||||
|
||||
let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
const setupFeatures = async (
|
||||
db: ITestDb,
|
||||
app: IUnleashTest,
|
||||
project = 'default',
|
||||
) => {
|
||||
await app.createFeature('test1', project);
|
||||
await app.createFeature('test2', project);
|
||||
|
||||
await app.addStrategyToFeatureEnv(
|
||||
{
|
||||
name: 'flexibleRollout',
|
||||
constraints: [],
|
||||
parameters: {
|
||||
rollout: '100',
|
||||
stickiness: 'default',
|
||||
groupId: 'test1',
|
||||
},
|
||||
},
|
||||
DEFAULT_ENV,
|
||||
'test1',
|
||||
project,
|
||||
);
|
||||
await app.addStrategyToFeatureEnv(
|
||||
{
|
||||
name: 'default',
|
||||
constraints: [
|
||||
{ contextName: 'userId', operator: 'IN', values: ['123'] },
|
||||
],
|
||||
parameters: {},
|
||||
},
|
||||
DEFAULT_ENV,
|
||||
'test2',
|
||||
project,
|
||||
);
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('client_feature_toggles_delta', getLogger);
|
||||
app = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
deltaApi: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
db.rawDatabase,
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.stores.eventStore.deleteAll();
|
||||
await db.stores.featureToggleStore.deleteAll();
|
||||
// @ts-ignore
|
||||
app.services.clientFeatureToggleService.clientFeatureToggleDelta.resetDelta();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.destroy();
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
test('should match with /api/client/delta', async () => {
|
||||
await setupFeatures(db, app);
|
||||
|
||||
const { body } = await app.request
|
||||
.get('/api/client/features')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
const { body: deltaBody } = await app.request
|
||||
.get('/api/client/delta')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body.features).toMatchObject(deltaBody.updated);
|
||||
});
|
||||
|
||||
test('should get 304 if asked for latest revision', async () => {
|
||||
await setupFeatures(db, app);
|
||||
|
||||
const { body } = await app.request.get('/api/client/delta').expect(200);
|
||||
const currentRevisionId = body.revisionId;
|
||||
|
||||
await app.request
|
||||
.set('If-None-Match', currentRevisionId)
|
||||
.get('/api/client/delta')
|
||||
.expect(304);
|
||||
});
|
||||
|
||||
test('should return correct delta after feature created', 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.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',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
const syncRevisions = async () => {
|
||||
await app.services.configurationRevisionService.updateMaxRevisionId();
|
||||
// @ts-ignore
|
||||
await app.services.clientFeatureToggleService.clientFeatureToggleDelta.onUpdateRevisionEvent();
|
||||
};
|
Loading…
Reference in New Issue
Block a user