mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-15 01:16:22 +02:00
feat: project insights resource with hardcoded data (#6610)
This commit is contained in:
parent
f3506da925
commit
03a84e2d42
@ -20,16 +20,14 @@ import {
|
||||
deprecatedProjectOverviewSchema,
|
||||
type ProjectDoraMetricsSchema,
|
||||
projectDoraMetricsSchema,
|
||||
projectInsightsSchema,
|
||||
type ProjectInsightsSchema,
|
||||
projectOverviewSchema,
|
||||
type ProjectsSchema,
|
||||
projectsSchema,
|
||||
} from '../../openapi';
|
||||
import { getStandardResponses } from '../../openapi/util/standard-responses';
|
||||
import type {
|
||||
AccessService,
|
||||
OpenApiService,
|
||||
SettingService,
|
||||
} from '../../services';
|
||||
import type { OpenApiService } from '../../services';
|
||||
import type { IAuthRequest } from '../../routes/unleash-types';
|
||||
import { ProjectApiTokenController } from '../../routes/admin-api/project/api-token';
|
||||
import ProjectArchiveController from '../../routes/admin-api/project/project-archive';
|
||||
@ -48,10 +46,6 @@ import { normalizeQueryParams } from '../feature-search/search-utils';
|
||||
export default class ProjectController extends Controller {
|
||||
private projectService: ProjectService;
|
||||
|
||||
private settingService: SettingService;
|
||||
|
||||
private accessService: AccessService;
|
||||
|
||||
private openApiService: OpenApiService;
|
||||
|
||||
private flagResolver: IFlagResolver;
|
||||
@ -60,8 +54,6 @@ export default class ProjectController extends Controller {
|
||||
super(config);
|
||||
this.projectService = services.projectService;
|
||||
this.openApiService = services.openApiService;
|
||||
this.settingService = services.settingService;
|
||||
this.accessService = services.accessService;
|
||||
this.flagResolver = config.flagResolver;
|
||||
|
||||
this.route({
|
||||
@ -127,6 +119,26 @@ export default class ProjectController extends Controller {
|
||||
],
|
||||
});
|
||||
|
||||
this.route({
|
||||
method: 'get',
|
||||
path: '/:projectId/insights',
|
||||
handler: this.getProjectInsights,
|
||||
permission: NONE,
|
||||
middleware: [
|
||||
this.openApiService.validPath({
|
||||
tags: ['Unstable'],
|
||||
operationId: 'getProjectInsights',
|
||||
summary: 'Get an overview of a project insights.',
|
||||
description:
|
||||
'This endpoint returns insights into the specified projects stats, health, lead time for changes, feature types used, members and change requests.',
|
||||
responses: {
|
||||
200: createResponseSchema('projectInsightsSchema'),
|
||||
...getStandardResponses(401, 403, 404),
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
this.route({
|
||||
method: 'get',
|
||||
path: '/:projectId/dora',
|
||||
@ -232,6 +244,74 @@ export default class ProjectController extends Controller {
|
||||
);
|
||||
}
|
||||
|
||||
async getProjectInsights(
|
||||
req: IAuthRequest<IProjectParam, unknown, unknown, unknown>,
|
||||
res: Response<ProjectInsightsSchema>,
|
||||
): Promise<void> {
|
||||
const result = {
|
||||
stats: {
|
||||
avgTimeToProdCurrentWindow: 17.1,
|
||||
createdCurrentWindow: 3,
|
||||
createdPastWindow: 6,
|
||||
archivedCurrentWindow: 0,
|
||||
archivedPastWindow: 1,
|
||||
projectActivityCurrentWindow: 458,
|
||||
projectActivityPastWindow: 578,
|
||||
projectMembersAddedCurrentWindow: 0,
|
||||
},
|
||||
featureTypeCounts: [
|
||||
{
|
||||
type: 'experiment',
|
||||
count: 4,
|
||||
},
|
||||
{
|
||||
type: 'permission',
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
count: 24,
|
||||
},
|
||||
],
|
||||
leadTime: {
|
||||
projectAverage: 17.1,
|
||||
features: [
|
||||
{ name: 'feature1', timeToProduction: 120 },
|
||||
{ name: 'feature2', timeToProduction: 0 },
|
||||
{ name: 'feature3', timeToProduction: 33 },
|
||||
{ name: 'feature4', timeToProduction: 131 },
|
||||
{ name: 'feature5', timeToProduction: 2 },
|
||||
],
|
||||
},
|
||||
health: {
|
||||
rating: 80,
|
||||
activeCount: 23,
|
||||
potentiallyStaleCount: 3,
|
||||
staleCount: 5,
|
||||
},
|
||||
members: {
|
||||
active: 20,
|
||||
inactive: 3,
|
||||
totalPreviousMonth: 15,
|
||||
},
|
||||
changeRequests: {
|
||||
total: 24,
|
||||
approved: 5,
|
||||
applied: 2,
|
||||
rejected: 4,
|
||||
reviewRequired: 10,
|
||||
scheduled: 3,
|
||||
},
|
||||
};
|
||||
|
||||
this.openApiService.respondWithValidation(
|
||||
200,
|
||||
res,
|
||||
projectInsightsSchema.$id,
|
||||
serializeDates(result),
|
||||
);
|
||||
}
|
||||
|
||||
async getProjectOverview(
|
||||
req: IAuthRequest<IProjectParam, unknown, unknown, IArchivedQuery>,
|
||||
res: Response<ProjectOverviewSchema>,
|
||||
|
@ -287,3 +287,15 @@ test('response should include last seen at per environment for multiple environm
|
||||
|
||||
expect(body.features[1].lastSeenAt).toBe('2023-10-01T12:34:56.000Z');
|
||||
});
|
||||
|
||||
test('project insights happy path', async () => {
|
||||
const { body } = await app.request
|
||||
.get('/api/admin/projects/default/insights')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body.leadTime.features[0]).toEqual({
|
||||
name: 'feature1',
|
||||
timeToProduction: 120,
|
||||
});
|
||||
});
|
||||
|
@ -134,6 +134,7 @@ export * from './project-application-sdk-schema';
|
||||
export * from './project-applications-schema';
|
||||
export * from './project-dora-metrics-schema';
|
||||
export * from './project-environment-schema';
|
||||
export * from './project-insights-schema';
|
||||
export * from './project-overview-schema';
|
||||
export * from './project-schema';
|
||||
export * from './project-stats-schema';
|
||||
|
148
src/lib/openapi/spec/project-insights-schema.ts
Normal file
148
src/lib/openapi/spec/project-insights-schema.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import type { FromSchema } from 'json-schema-to-ts';
|
||||
import { projectStatsSchema } from './project-stats-schema';
|
||||
import { featureTypeCountSchema } from './feature-type-count-schema';
|
||||
import { doraFeaturesSchema } from './dora-features-schema';
|
||||
import { projectDoraMetricsSchema } from './project-dora-metrics-schema';
|
||||
|
||||
export const projectInsightsSchema = {
|
||||
$id: '#/components/schemas/projectInsightsSchema',
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['stats', 'leadTime', 'featureTypeCounts', 'health', 'members'],
|
||||
description:
|
||||
'A high-level overview of a project insights. It contains information such as project statistics, overall health, types of flags, members overview, change requests overview.',
|
||||
properties: {
|
||||
stats: {
|
||||
$ref: '#/components/schemas/projectStatsSchema',
|
||||
description: 'Project statistics',
|
||||
},
|
||||
health: {
|
||||
type: 'object',
|
||||
required: [
|
||||
'rating',
|
||||
'activeCount',
|
||||
'potentiallyStaleCount',
|
||||
'staleCount',
|
||||
],
|
||||
properties: {
|
||||
rating: {
|
||||
type: 'integer',
|
||||
description:
|
||||
"An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#health-rating) on a scale from 0 to 100",
|
||||
example: 95,
|
||||
},
|
||||
activeCount: {
|
||||
type: 'number',
|
||||
description: 'The number of active feature toggles.',
|
||||
example: 12,
|
||||
},
|
||||
potentiallyStaleCount: {
|
||||
type: 'number',
|
||||
description:
|
||||
'The number of potentially stale feature toggles.',
|
||||
example: 5,
|
||||
},
|
||||
staleCount: {
|
||||
type: 'number',
|
||||
description: 'The number of stale feature toggles.',
|
||||
example: 10,
|
||||
},
|
||||
},
|
||||
description: 'Health summary of the project',
|
||||
},
|
||||
leadTime: {
|
||||
type: 'object',
|
||||
$ref: '#/components/schemas/projectDoraMetricsSchema',
|
||||
description: 'Lead time (DORA) metrics',
|
||||
},
|
||||
featureTypeCounts: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/featureTypeCountSchema',
|
||||
},
|
||||
description: 'The number of features of each type',
|
||||
},
|
||||
members: {
|
||||
type: 'object',
|
||||
required: ['active', 'inactive'],
|
||||
properties: {
|
||||
active: {
|
||||
type: 'number',
|
||||
description:
|
||||
'The number of active project members who have used Unleash in the past 60 days',
|
||||
example: 10,
|
||||
},
|
||||
inactive: {
|
||||
type: 'number',
|
||||
description:
|
||||
'The number of inactive project members who have not used Unleash in the past 60 days',
|
||||
example: 10,
|
||||
},
|
||||
totalPreviousMonth: {
|
||||
type: 'number',
|
||||
description:
|
||||
'The number of total project members in the previous month',
|
||||
example: 8,
|
||||
},
|
||||
},
|
||||
description: 'Active/inactive users summary',
|
||||
},
|
||||
changeRequests: {
|
||||
type: 'object',
|
||||
required: [
|
||||
'total',
|
||||
'applied',
|
||||
'rejected',
|
||||
'reviewRequired',
|
||||
'approved',
|
||||
'scheduled',
|
||||
],
|
||||
properties: {
|
||||
total: {
|
||||
type: 'number',
|
||||
description:
|
||||
'The number of total change requests in this project',
|
||||
example: 10,
|
||||
},
|
||||
applied: {
|
||||
type: 'number',
|
||||
description: 'The number of applied change requests',
|
||||
example: 5,
|
||||
},
|
||||
rejected: {
|
||||
type: 'number',
|
||||
description: 'The number of rejected change requests',
|
||||
example: 2,
|
||||
},
|
||||
reviewRequired: {
|
||||
type: 'number',
|
||||
description:
|
||||
'The number of change requests awaiting the review',
|
||||
example: 2,
|
||||
},
|
||||
approved: {
|
||||
type: 'number',
|
||||
description: 'The number of approved change requests',
|
||||
example: 1,
|
||||
},
|
||||
scheduled: {
|
||||
type: 'number',
|
||||
description: 'The number of scheduled change requests',
|
||||
example: 1,
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Count of change requests in different stages of the [process](https://docs.getunleash.io/reference/change-requests#change-request-flow). Only for enterprise users.',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
projectStatsSchema,
|
||||
featureTypeCountSchema,
|
||||
projectDoraMetricsSchema,
|
||||
doraFeaturesSchema,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type ProjectInsightsSchema = FromSchema<typeof projectInsightsSchema>;
|
Loading…
Reference in New Issue
Block a user