1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-20 00:08:02 +01:00

feat: application missing features backend (#6330)

This PR adds a property issues to application schema, and also adds all
the missing features that have been reported by SDK, but do not exist in
Unleash.
This commit is contained in:
Jaanus Sellin 2024-02-26 12:26:01 +02:00 committed by GitHub
parent d1e93228a3
commit 89d113f1ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 87 additions and 11 deletions

View File

@ -10,6 +10,7 @@ import { Logger, LogProvider } from '../logger';
import { Db } from './db';
import { IApplicationOverview } from '../features/metrics/instance/models';
import { applySearchFilters } from '../features/feature-search/search-utils';
import { ApplicationOverviewIssuesSchema } from '../openapi/spec/application-overview-issues-schema';
const COLUMNS = [
'app_name',
@ -305,7 +306,6 @@ export default class ClientApplicationsStore
);
})
.where('a.app_name', appName);
const rows = await query;
if (!rows.length) {
throw new NotFoundError(`Could not find appName=${appName}`);
@ -316,22 +316,41 @@ export default class ClientApplicationsStore
mapApplicationOverviewData(rows: any[]): IApplicationOverview {
const featureCount = new Set(rows.map((row) => row.feature_name)).size;
const missingFeatures: Set<string> = new Set();
const environments = rows.reduce((acc, row) => {
const { environment, instance_id, sdk_version, last_seen } = row;
const {
environment,
instance_id,
sdk_version,
last_seen,
project,
feature_name,
} = row;
if (!environment) return acc;
if (!project && feature_name) {
missingFeatures.add(feature_name);
}
let env = acc.find((e) => e.name === environment);
if (!env) {
env = {
name: environment,
instanceCount: 1,
instanceCount: instance_id ? 1 : 0,
sdks: sdk_version ? [sdk_version] : [],
lastSeen: last_seen,
uniqueInstanceIds: new Set([instance_id]),
uniqueInstanceIds: new Set(
instance_id ? [instance_id] : [],
),
};
acc.push(env);
} else {
env.uniqueInstanceIds.add(instance_id);
env.instanceCount = env.uniqueInstanceIds.size;
if (instance_id && !env.uniqueInstanceIds.has(instance_id)) {
env.uniqueInstanceIds.add(instance_id);
env.instanceCount = env.uniqueInstanceIds.size;
}
if (sdk_version && !env.sdks.includes(sdk_version)) {
env.sdks.push(sdk_version);
}
@ -342,12 +361,21 @@ export default class ClientApplicationsStore
return acc;
}, []);
environments.forEach((env) => {
delete env.uniqueInstanceIds;
env.sdks.sort();
});
const issues: ApplicationOverviewIssuesSchema[] =
missingFeatures.size > 0
? [
{
type: 'missingFeatures',
items: [...missingFeatures],
},
]
: [];
return {
projects: [
...new Set(
@ -358,6 +386,7 @@ export default class ClientApplicationsStore
],
featureCount,
environments,
issues,
};
}
}

View File

@ -9,7 +9,7 @@ export const applicationOverviewIssuesSchema = {
properties: {
type: {
type: 'string',
enum: ['missingFeature', 'missingStrategy'],
enum: ['missingFeatures', 'missingStrategies'],
description: 'The name of this action.',
},
items: {

View File

@ -5,8 +5,8 @@ test('applicationOverviewSchema', () => {
projects: ['default', 'dx'],
featureCount: 12,
issues: [
{ type: 'missingFeature', items: ['feature1'] },
{ type: 'missingStrategy', items: ['strategy1'] },
{ type: 'missingFeatures', items: ['feature1'] },
{ type: 'missingStrategies', items: ['strategy1'] },
],
environments: [
{

View File

@ -8,7 +8,7 @@ export const applicationOverviewSchema = {
description:
"Data about an application that's connected to Unleash via an SDK.",
additionalProperties: false,
required: ['projects', 'featureCount', 'environments'],
required: ['projects', 'featureCount', 'environments', 'issues'],
properties: {
projects: {
description: 'The list of projects the application has been using.',

View File

@ -122,6 +122,7 @@ test('should show correct number of total', async () => {
const expected = {
projects: ['default'],
issues: [],
environments: [
{
instanceCount: 2,
@ -134,3 +135,49 @@ test('should show correct number of total', async () => {
expect(body).toMatchObject(expected);
});
test('should show missing features', async () => {
await Promise.all([
app.createFeature('toggle-name-1'),
app.request.post('/api/client/register').send({
appName: metrics.appName,
instanceId: metrics.instanceId,
strategies: ['default'],
sdkVersion: 'unleash-client-test',
started: Date.now(),
interval: 10,
}),
]);
await app.services.clientInstanceService.bulkAdd();
await app.request
.post('/api/client/metrics')
.set('Authorization', defaultToken.secret)
.send(metrics)
.expect(202);
await app.services.clientMetricsServiceV2.bulkAdd();
const { body } = await app.request
.get(`/api/admin/metrics/applications/${metrics.appName}/overview`)
.expect(200);
const expected = {
projects: ['default'],
issues: [
{
type: 'missingFeatures',
items: ['toggle-name-2', 'toggle-name-3'],
},
],
environments: [
{
instanceCount: 1,
name: 'default',
sdks: ['unleash-client-test'],
},
],
featureCount: 3,
};
expect(body).toMatchObject(expected);
});