mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-22 01:16:07 +02:00
feat: add kill switch for client metrics (#4829)
This PR adds a killswitch for client metrics that we can use to disable metrics in our cloud offering.
This commit is contained in:
parent
d16334f3fb
commit
b919c445b4
@ -79,6 +79,7 @@ exports[`should create default config 1`] = `
|
|||||||
"demo": false,
|
"demo": false,
|
||||||
"dependentFeatures": false,
|
"dependentFeatures": false,
|
||||||
"disableBulkToggle": false,
|
"disableBulkToggle": false,
|
||||||
|
"disableMetrics": false,
|
||||||
"disableNotifications": false,
|
"disableNotifications": false,
|
||||||
"doraMetrics": false,
|
"doraMetrics": false,
|
||||||
"embedProxy": true,
|
"embedProxy": true,
|
||||||
@ -117,6 +118,7 @@ exports[`should create default config 1`] = `
|
|||||||
"demo": false,
|
"demo": false,
|
||||||
"dependentFeatures": false,
|
"dependentFeatures": false,
|
||||||
"disableBulkToggle": false,
|
"disableBulkToggle": false,
|
||||||
|
"disableMetrics": false,
|
||||||
"disableNotifications": false,
|
"disableNotifications": false,
|
||||||
"doraMetrics": false,
|
"doraMetrics": false,
|
||||||
"embedProxy": true,
|
"embedProxy": true,
|
||||||
|
@ -254,3 +254,32 @@ test('should return a 200 if required fields are there', async () => {
|
|||||||
})
|
})
|
||||||
.expect(202);
|
.expect(202);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should return 204 if metrics are disabled by feature flag', async () => {
|
||||||
|
const { request: localRequest } = await getSetup({
|
||||||
|
experimental: {
|
||||||
|
flags: {
|
||||||
|
disableMetrics: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await localRequest
|
||||||
|
.post('/api/client/metrics')
|
||||||
|
.send({
|
||||||
|
appName: 'demo',
|
||||||
|
someParam: 'some-value',
|
||||||
|
somOtherParam: 'some--other-value',
|
||||||
|
bucket: {
|
||||||
|
start: Date.now(),
|
||||||
|
stop: Date.now(),
|
||||||
|
toggles: {
|
||||||
|
toggleLastSeen: {
|
||||||
|
yes: 200,
|
||||||
|
no: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.expect(204);
|
||||||
|
});
|
||||||
|
@ -58,6 +58,7 @@ export default class ClientMetricsController extends Controller {
|
|||||||
responses: {
|
responses: {
|
||||||
...getStandardResponses(400),
|
...getStandardResponses(400),
|
||||||
202: emptyResponse,
|
202: emptyResponse,
|
||||||
|
204: emptyResponse,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -65,13 +66,19 @@ export default class ClientMetricsController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async registerMetrics(req: IAuthRequest, res: Response): Promise<void> {
|
async registerMetrics(req: IAuthRequest, res: Response): Promise<void> {
|
||||||
|
if (this.config.flagResolver.isEnabled('disableMetrics')) {
|
||||||
|
res.status(204).end();
|
||||||
|
} else {
|
||||||
try {
|
try {
|
||||||
const { body: data, ip: clientIp, user } = req;
|
const { body: data, ip: clientIp, user } = req;
|
||||||
data.environment = this.metricsV2.resolveMetricsEnvironment(
|
data.environment = this.metricsV2.resolveMetricsEnvironment(
|
||||||
user,
|
user,
|
||||||
data,
|
data,
|
||||||
);
|
);
|
||||||
await this.clientInstanceService.registerInstance(data, clientIp);
|
await this.clientInstanceService.registerInstance(
|
||||||
|
data,
|
||||||
|
clientIp,
|
||||||
|
);
|
||||||
|
|
||||||
await this.metricsV2.registerClientMetrics(data, clientIp);
|
await this.metricsV2.registerClientMetrics(data, clientIp);
|
||||||
res.status(202).end();
|
res.status(202).end();
|
||||||
@ -79,4 +86,5 @@ export default class ClientMetricsController extends Controller {
|
|||||||
res.status(400).end();
|
res.status(400).end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,7 @@ export default class ProxyController extends Controller {
|
|||||||
requestBody: createRequestSchema('clientMetricsSchema'),
|
requestBody: createRequestSchema('clientMetricsSchema'),
|
||||||
responses: {
|
responses: {
|
||||||
200: emptyResponse,
|
200: emptyResponse,
|
||||||
|
204: emptyResponse,
|
||||||
...getStandardResponses(400, 401, 404),
|
...getStandardResponses(400, 401, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -189,6 +190,12 @@ export default class ProxyController extends Controller {
|
|||||||
if (!this.config.flagResolver.isEnabled('embedProxy')) {
|
if (!this.config.flagResolver.isEnabled('embedProxy')) {
|
||||||
throw new NotFoundError();
|
throw new NotFoundError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.flagResolver.isEnabled('disableMetrics')) {
|
||||||
|
res.sendStatus(204);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await this.services.proxyService.registerProxyMetrics(
|
await this.services.proxyService.registerProxyMetrics(
|
||||||
req.user,
|
req.user,
|
||||||
req.body,
|
req.body,
|
||||||
|
@ -30,7 +30,8 @@ export type IFlagKey =
|
|||||||
| 'accessOverview'
|
| 'accessOverview'
|
||||||
| 'privateProjects'
|
| 'privateProjects'
|
||||||
| 'dependentFeatures'
|
| 'dependentFeatures'
|
||||||
| 'datadogJsonTemplate';
|
| 'datadogJsonTemplate'
|
||||||
|
| 'disableMetrics';
|
||||||
|
|
||||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||||
|
|
||||||
@ -142,6 +143,10 @@ const flags: IFlags = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_DATADOG_JSON_TEMPLATE,
|
process.env.UNLEASH_EXPERIMENTAL_DATADOG_JSON_TEMPLATE,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
disableMetrics: parseEnvVarBoolean(
|
||||||
|
process.env.UNLEASH_EXPERIMENTAL_DISABLE_METRICS,
|
||||||
|
false,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||||
|
@ -1193,3 +1193,42 @@ test('should NOT evaluate disabled strategies when returning toggles', async ()
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should return 204 if metrics are disabled', async () => {
|
||||||
|
const localApp = await setupAppWithAuth(db.stores, {
|
||||||
|
frontendApiOrigins: ['https://example.com'],
|
||||||
|
experimental: {
|
||||||
|
flags: {
|
||||||
|
disableMetrics: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const frontendToken =
|
||||||
|
await localApp.services.apiTokenService.createApiTokenWithProjects({
|
||||||
|
type: ApiTokenType.FRONTEND,
|
||||||
|
projects: ['*'],
|
||||||
|
environment: 'default',
|
||||||
|
tokenName: `disabledMetric-token-${randomId()}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const appName = randomId();
|
||||||
|
const instanceId = randomId();
|
||||||
|
const featureName = 'metricsDisabled';
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
await localApp.request
|
||||||
|
.post('/api/frontend/client/metrics')
|
||||||
|
.set('Authorization', frontendToken.secret)
|
||||||
|
.send({
|
||||||
|
appName,
|
||||||
|
instanceId,
|
||||||
|
bucket: {
|
||||||
|
start: now,
|
||||||
|
stop: now,
|
||||||
|
toggles: { [featureName]: { yes: 2, no: 20 } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.expect(204);
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user