mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-19 17:52:45 +02:00
Add etagByEnv feature and corresponding tests
This commit is contained in:
parent
a575a792b3
commit
0da567dd9b
@ -357,9 +357,10 @@ export default class FeatureController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async calculateMeta(query: IFeatureToggleQuery): Promise<IMeta> {
|
async calculateMeta(query: IFeatureToggleQuery): Promise<IMeta> {
|
||||||
|
const etagByEnvEnabled = this.flagResolver.isEnabled('etagByEnv');
|
||||||
const revisionId =
|
const revisionId =
|
||||||
await this.configurationRevisionService.getMaxRevisionId(
|
await this.configurationRevisionService.getMaxRevisionId(
|
||||||
query.environment,
|
etagByEnvEnabled ? query.environment : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const queryHash = hashSum(query);
|
const queryHash = hashSum(query);
|
||||||
|
@ -67,7 +67,8 @@ export type IFlagKey =
|
|||||||
| 'lifecycleGraphs'
|
| 'lifecycleGraphs'
|
||||||
| 'addConfiguration'
|
| 'addConfiguration'
|
||||||
| 'filterFlagsToArchive'
|
| 'filterFlagsToArchive'
|
||||||
| 'projectListViewToggle';
|
| 'projectListViewToggle'
|
||||||
|
| 'etagByEnv';
|
||||||
|
|
||||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||||
|
|
||||||
|
@ -39,11 +39,11 @@ const prodTokenSecret = validTokens[1].secret;
|
|||||||
const allEnvsTokenSecret = validTokens[2].secret;
|
const allEnvsTokenSecret = validTokens[2].secret;
|
||||||
|
|
||||||
async function setup({
|
async function setup({
|
||||||
etagVariantName,
|
etagVariant,
|
||||||
enabled,
|
etagByEnvEnabled,
|
||||||
}: {
|
}: {
|
||||||
etagVariantName: string;
|
etagVariant: string | undefined;
|
||||||
enabled: boolean;
|
etagByEnvEnabled: boolean;
|
||||||
}): Promise<{ app: IUnleashTest; db: ITestDb }> {
|
}): Promise<{ app: IUnleashTest; db: ITestDb }> {
|
||||||
const db = await dbInit(`ignored`, getLogger);
|
const db = await dbInit(`ignored`, getLogger);
|
||||||
|
|
||||||
@ -59,10 +59,11 @@ async function setup({
|
|||||||
flags: {
|
flags: {
|
||||||
strictSchemaValidation: true,
|
strictSchemaValidation: true,
|
||||||
etagVariant: {
|
etagVariant: {
|
||||||
name: etagVariantName,
|
name: etagVariant,
|
||||||
enabled,
|
enabled: etagVariant !== undefined,
|
||||||
feature_enabled: enabled,
|
feature_enabled: etagVariant !== undefined,
|
||||||
},
|
},
|
||||||
|
etagByEnv: etagByEnvEnabled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -176,20 +177,29 @@ async function validateInitialState({
|
|||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
{
|
{
|
||||||
name: 'disabled',
|
etagVariant: undefined,
|
||||||
enabled: false,
|
etagByEnvEnabled: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'v2',
|
etagVariant: 'v2',
|
||||||
enabled: true,
|
etagByEnvEnabled: false,
|
||||||
},
|
},
|
||||||
])('feature 304 api client (etag variant = $name)', ({ name, enabled }) => {
|
{
|
||||||
|
etagVariant: 'v2',
|
||||||
|
etagByEnvEnabled: true,
|
||||||
|
},
|
||||||
|
])(
|
||||||
|
'feature 304 api client (etag variant = $etagVariant)',
|
||||||
|
({ etagVariant, etagByEnvEnabled }) => {
|
||||||
let app: IUnleashTest;
|
let app: IUnleashTest;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
const etagVariantEnabled = etagVariant !== undefined;
|
||||||
|
const etagVariantName = etagVariant ?? 'disabled';
|
||||||
|
const expectedDevEventId = etagByEnvEnabled ? 13 : 15;
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
({ app, db } = await setup({
|
({ app, db } = await setup({
|
||||||
etagVariantName: name,
|
etagVariant,
|
||||||
enabled,
|
etagByEnvEnabled,
|
||||||
}));
|
}));
|
||||||
await initialize({ app, db });
|
await initialize({ app, db });
|
||||||
await validateInitialState({ app, db });
|
await validateInitialState({ app, db });
|
||||||
@ -207,25 +217,37 @@ describe.each([
|
|||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
if (enabled) {
|
if (etagVariantEnabled) {
|
||||||
expect(res.headers.etag).toBe(`"76d8bb0e:13:${name}"`);
|
expect(res.headers.etag).toBe(
|
||||||
expect(res.body.meta.etag).toBe(`"76d8bb0e:13:${name}"`);
|
`"76d8bb0e:${expectedDevEventId}:${etagVariantName}"`,
|
||||||
|
);
|
||||||
|
expect(res.body.meta.etag).toBe(
|
||||||
|
`"76d8bb0e:${expectedDevEventId}:${etagVariantName}"`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(res.headers.etag).toBe('"76d8bb0e:13"');
|
expect(res.headers.etag).toBe(
|
||||||
expect(res.body.meta.etag).toBe('"76d8bb0e:13"');
|
`"76d8bb0e:${expectedDevEventId}"`,
|
||||||
|
);
|
||||||
|
expect(res.body.meta.etag).toBe(
|
||||||
|
`"76d8bb0e:${expectedDevEventId}"`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`returns ${enabled ? 200 : 304} for pre-calculated hash${enabled ? ' because hash changed' : ''} (dev env token)`, async () => {
|
test(`returns ${etagVariantEnabled ? 200 : 304} for pre-calculated hash${etagVariantEnabled ? ' because hash changed' : ''} (dev env token)`, async () => {
|
||||||
const res = await app.request
|
const res = await app.request
|
||||||
.get('/api/client/features')
|
.get('/api/client/features')
|
||||||
.set('Authorization', devTokenSecret)
|
.set('Authorization', devTokenSecret)
|
||||||
.set('if-none-match', '"76d8bb0e:13"')
|
.set('if-none-match', `"76d8bb0e:${expectedDevEventId}"`)
|
||||||
.expect(enabled ? 200 : 304);
|
.expect(etagVariantEnabled ? 200 : 304);
|
||||||
|
|
||||||
if (enabled) {
|
if (etagVariantEnabled) {
|
||||||
expect(res.headers.etag).toBe(`"76d8bb0e:13:${name}"`);
|
expect(res.headers.etag).toBe(
|
||||||
expect(res.body.meta.etag).toBe(`"76d8bb0e:13:${name}"`);
|
`"76d8bb0e:${expectedDevEventId}:${etagVariantName}"`,
|
||||||
|
);
|
||||||
|
expect(res.body.meta.etag).toBe(
|
||||||
|
`"76d8bb0e:${expectedDevEventId}:${etagVariantName}"`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -236,12 +258,15 @@ describe.each([
|
|||||||
await app.request
|
await app.request
|
||||||
.get('/api/client/features')
|
.get('/api/client/features')
|
||||||
.set('Authorization', devTokenSecret)
|
.set('Authorization', devTokenSecret)
|
||||||
.set('if-none-match', `"76d8bb0e:13${enabled ? `:${name}` : ''}"`)
|
.set(
|
||||||
|
'if-none-match',
|
||||||
|
`"76d8bb0e:${expectedDevEventId}${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
|
)
|
||||||
.expect(304);
|
.expect(304);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('a token with all envs should get the max id regardless of the environment', async () => {
|
test('a token with all envs should get the max id regardless of the environment', async () => {
|
||||||
const currentProdEtag = `"67e24428:15${enabled ? `:${name}` : ''}"`;
|
const currentProdEtag = `"67e24428:15${etagVariantEnabled ? `:${etagVariantName}` : ''}"`;
|
||||||
const { headers } = await app.request
|
const { headers } = await app.request
|
||||||
.get('/api/client/features')
|
.get('/api/client/features')
|
||||||
.set('if-none-match', currentProdEtag)
|
.set('if-none-match', currentProdEtag)
|
||||||
@ -250,18 +275,20 @@ describe.each([
|
|||||||
|
|
||||||
// it's a different hash than prod, but gets the max id
|
// it's a different hash than prod, but gets the max id
|
||||||
expect(headers.etag).toEqual(
|
expect(headers.etag).toEqual(
|
||||||
`"ae443048:15${enabled ? `:${name}` : ''}"`,
|
`"ae443048:15${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('production environment gets a different etag than development', async () => {
|
test.runIf(!etagByEnvEnabled)(
|
||||||
|
'production environment gets same event id in etag than development',
|
||||||
|
async () => {
|
||||||
const { headers: prodHeaders } = await app.request
|
const { headers: prodHeaders } = await app.request
|
||||||
.get('/api/client/features?bla=1')
|
.get('/api/client/features?bla=1')
|
||||||
.set('Authorization', prodTokenSecret)
|
.set('Authorization', prodTokenSecret)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
expect(prodHeaders.etag).toEqual(
|
expect(prodHeaders.etag).toEqual(
|
||||||
`"67e24428:15${enabled ? `:${name}` : ''}"`,
|
`"67e24428:15${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { headers: devHeaders } = await app.request
|
const { headers: devHeaders } = await app.request
|
||||||
@ -270,13 +297,80 @@ describe.each([
|
|||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
expect(devHeaders.etag).toEqual(
|
expect(devHeaders.etag).toEqual(
|
||||||
`"76d8bb0e:13${enabled ? `:${name}` : ''}"`,
|
`"76d8bb0e:15${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
test('modifying dev environment should only invalidate dev tokens', async () => {
|
test.runIf(!etagByEnvEnabled)(
|
||||||
const currentDevEtag = `"76d8bb0e:13${enabled ? `:${name}` : ''}"`;
|
'modifying dev environment also invalidates prod tokens',
|
||||||
const currentProdEtag = `"67e24428:15${enabled ? `:${name}` : ''}"`;
|
async () => {
|
||||||
|
const currentDevEtag = `"76d8bb0e:${expectedDevEventId}${etagVariantEnabled ? `:${etagVariantName}` : ''}"`;
|
||||||
|
const currentProdEtag = `"67e24428:15${etagVariantEnabled ? `:${etagVariantName}` : ''}"`;
|
||||||
|
await app.request
|
||||||
|
.get('/api/client/features')
|
||||||
|
.set('if-none-match', currentProdEtag)
|
||||||
|
.set('Authorization', prodTokenSecret)
|
||||||
|
.expect(304);
|
||||||
|
|
||||||
|
await app.request
|
||||||
|
.get('/api/client/features')
|
||||||
|
.set('Authorization', devTokenSecret)
|
||||||
|
.set('if-none-match', currentDevEtag)
|
||||||
|
.expect(304);
|
||||||
|
|
||||||
|
await app.enableFeature('X', DEFAULT_ENV);
|
||||||
|
await app.services.configurationRevisionService.updateMaxRevisionId();
|
||||||
|
|
||||||
|
await app.request
|
||||||
|
.get('/api/client/features')
|
||||||
|
.set('Authorization', prodTokenSecret)
|
||||||
|
.set('if-none-match', currentProdEtag)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const { headers: devHeaders } = await app.request
|
||||||
|
.get('/api/client/features')
|
||||||
|
.set('Authorization', devTokenSecret)
|
||||||
|
.set('if-none-match', currentDevEtag)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
// Note: this test yields a different result if run in isolation
|
||||||
|
// this is because the id 19 depends on a previous test adding a feature
|
||||||
|
// otherwise the id will be 18
|
||||||
|
expect(devHeaders.etag).toEqual(
|
||||||
|
`"76d8bb0e:19${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.runIf(etagByEnvEnabled)(
|
||||||
|
'production environment gets a different etag than development',
|
||||||
|
async () => {
|
||||||
|
const { headers: prodHeaders } = await app.request
|
||||||
|
.get('/api/client/features?bla=1')
|
||||||
|
.set('Authorization', prodTokenSecret)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(prodHeaders.etag).toEqual(
|
||||||
|
`"67e24428:15${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { headers: devHeaders } = await app.request
|
||||||
|
.get('/api/client/features')
|
||||||
|
.set('Authorization', devTokenSecret)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(devHeaders.etag).toEqual(
|
||||||
|
`"76d8bb0e:13${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.runIf(etagByEnvEnabled)(
|
||||||
|
'modifying dev environment should only invalidate dev tokens',
|
||||||
|
async () => {
|
||||||
|
const currentDevEtag = `"76d8bb0e:13${etagVariantEnabled ? `:${etagVariantName}` : ''}"`;
|
||||||
|
const currentProdEtag = `"67e24428:15${etagVariantEnabled ? `:${etagVariantName}` : ''}"`;
|
||||||
await app.request
|
await app.request
|
||||||
.get('/api/client/features')
|
.get('/api/client/features')
|
||||||
.set('if-none-match', currentProdEtag)
|
.set('if-none-match', currentProdEtag)
|
||||||
@ -308,7 +402,9 @@ describe.each([
|
|||||||
// this is because the id 19 depends on a previous test adding a feature
|
// this is because the id 19 depends on a previous test adding a feature
|
||||||
// otherwise the id will be 18
|
// otherwise the id will be 18
|
||||||
expect(devHeaders.etag).toEqual(
|
expect(devHeaders.etag).toEqual(
|
||||||
`"76d8bb0e:19${enabled ? `:${name}` : ''}"`,
|
`"76d8bb0e:19${etagVariantEnabled ? `:${etagVariantName}` : ''}"`,
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user