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:
parent
d1e93228a3
commit
89d113f1ff
@ -10,6 +10,7 @@ import { Logger, LogProvider } from '../logger';
|
|||||||
import { Db } from './db';
|
import { Db } from './db';
|
||||||
import { IApplicationOverview } from '../features/metrics/instance/models';
|
import { IApplicationOverview } from '../features/metrics/instance/models';
|
||||||
import { applySearchFilters } from '../features/feature-search/search-utils';
|
import { applySearchFilters } from '../features/feature-search/search-utils';
|
||||||
|
import { ApplicationOverviewIssuesSchema } from '../openapi/spec/application-overview-issues-schema';
|
||||||
|
|
||||||
const COLUMNS = [
|
const COLUMNS = [
|
||||||
'app_name',
|
'app_name',
|
||||||
@ -305,7 +306,6 @@ export default class ClientApplicationsStore
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
.where('a.app_name', appName);
|
.where('a.app_name', appName);
|
||||||
|
|
||||||
const rows = await query;
|
const rows = await query;
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
throw new NotFoundError(`Could not find appName=${appName}`);
|
throw new NotFoundError(`Could not find appName=${appName}`);
|
||||||
@ -316,22 +316,41 @@ export default class ClientApplicationsStore
|
|||||||
|
|
||||||
mapApplicationOverviewData(rows: any[]): IApplicationOverview {
|
mapApplicationOverviewData(rows: any[]): IApplicationOverview {
|
||||||
const featureCount = new Set(rows.map((row) => row.feature_name)).size;
|
const featureCount = new Set(rows.map((row) => row.feature_name)).size;
|
||||||
|
const missingFeatures: Set<string> = new Set();
|
||||||
|
|
||||||
const environments = rows.reduce((acc, row) => {
|
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);
|
let env = acc.find((e) => e.name === environment);
|
||||||
if (!env) {
|
if (!env) {
|
||||||
env = {
|
env = {
|
||||||
name: environment,
|
name: environment,
|
||||||
instanceCount: 1,
|
instanceCount: instance_id ? 1 : 0,
|
||||||
sdks: sdk_version ? [sdk_version] : [],
|
sdks: sdk_version ? [sdk_version] : [],
|
||||||
lastSeen: last_seen,
|
lastSeen: last_seen,
|
||||||
uniqueInstanceIds: new Set([instance_id]),
|
uniqueInstanceIds: new Set(
|
||||||
|
instance_id ? [instance_id] : [],
|
||||||
|
),
|
||||||
};
|
};
|
||||||
acc.push(env);
|
acc.push(env);
|
||||||
} else {
|
} else {
|
||||||
env.uniqueInstanceIds.add(instance_id);
|
if (instance_id && !env.uniqueInstanceIds.has(instance_id)) {
|
||||||
env.instanceCount = env.uniqueInstanceIds.size;
|
env.uniqueInstanceIds.add(instance_id);
|
||||||
|
env.instanceCount = env.uniqueInstanceIds.size;
|
||||||
|
}
|
||||||
if (sdk_version && !env.sdks.includes(sdk_version)) {
|
if (sdk_version && !env.sdks.includes(sdk_version)) {
|
||||||
env.sdks.push(sdk_version);
|
env.sdks.push(sdk_version);
|
||||||
}
|
}
|
||||||
@ -342,12 +361,21 @@ export default class ClientApplicationsStore
|
|||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
environments.forEach((env) => {
|
environments.forEach((env) => {
|
||||||
delete env.uniqueInstanceIds;
|
delete env.uniqueInstanceIds;
|
||||||
env.sdks.sort();
|
env.sdks.sort();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const issues: ApplicationOverviewIssuesSchema[] =
|
||||||
|
missingFeatures.size > 0
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
type: 'missingFeatures',
|
||||||
|
items: [...missingFeatures],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projects: [
|
projects: [
|
||||||
...new Set(
|
...new Set(
|
||||||
@ -358,6 +386,7 @@ export default class ClientApplicationsStore
|
|||||||
],
|
],
|
||||||
featureCount,
|
featureCount,
|
||||||
environments,
|
environments,
|
||||||
|
issues,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ export const applicationOverviewIssuesSchema = {
|
|||||||
properties: {
|
properties: {
|
||||||
type: {
|
type: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: ['missingFeature', 'missingStrategy'],
|
enum: ['missingFeatures', 'missingStrategies'],
|
||||||
description: 'The name of this action.',
|
description: 'The name of this action.',
|
||||||
},
|
},
|
||||||
items: {
|
items: {
|
||||||
|
@ -5,8 +5,8 @@ test('applicationOverviewSchema', () => {
|
|||||||
projects: ['default', 'dx'],
|
projects: ['default', 'dx'],
|
||||||
featureCount: 12,
|
featureCount: 12,
|
||||||
issues: [
|
issues: [
|
||||||
{ type: 'missingFeature', items: ['feature1'] },
|
{ type: 'missingFeatures', items: ['feature1'] },
|
||||||
{ type: 'missingStrategy', items: ['strategy1'] },
|
{ type: 'missingStrategies', items: ['strategy1'] },
|
||||||
],
|
],
|
||||||
environments: [
|
environments: [
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ export const applicationOverviewSchema = {
|
|||||||
description:
|
description:
|
||||||
"Data about an application that's connected to Unleash via an SDK.",
|
"Data about an application that's connected to Unleash via an SDK.",
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['projects', 'featureCount', 'environments'],
|
required: ['projects', 'featureCount', 'environments', 'issues'],
|
||||||
properties: {
|
properties: {
|
||||||
projects: {
|
projects: {
|
||||||
description: 'The list of projects the application has been using.',
|
description: 'The list of projects the application has been using.',
|
||||||
|
@ -122,6 +122,7 @@ test('should show correct number of total', async () => {
|
|||||||
|
|
||||||
const expected = {
|
const expected = {
|
||||||
projects: ['default'],
|
projects: ['default'],
|
||||||
|
issues: [],
|
||||||
environments: [
|
environments: [
|
||||||
{
|
{
|
||||||
instanceCount: 2,
|
instanceCount: 2,
|
||||||
@ -134,3 +135,49 @@ test('should show correct number of total', async () => {
|
|||||||
|
|
||||||
expect(body).toMatchObject(expected);
|
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);
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user