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

feat: outdated sdks project level (#7080)

This adds project level endpoint to catch outdated SDKs only for that
project.
This commit is contained in:
Jaanus Sellin 2024-05-20 12:58:30 +03:00 committed by GitHub
parent 50ee7fa779
commit a204f2c615
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 1 deletions

View File

@ -218,6 +218,22 @@ export default class ClientInstanceStore implements IClientInstanceStore {
return rows; return rows;
} }
async groupApplicationsBySdkAndProject(
projectId: string,
): Promise<{ sdkVersion: string; applications: string[] }[]> {
const rows = await this.db
.select([
'ci.sdk_version as sdkVersion',
this.db.raw('ARRAY_AGG(DISTINCT cme.app_name) as applications'),
])
.from('client_metrics_env as cme')
.leftJoin('features as f', 'f.name', 'cme.feature_name')
.leftJoin('client_instances as ci', 'ci.app_name', 'cme.app_name')
.where('f.project', projectId)
.groupBy('ci.sdk_version');
return rows;
}
async getDistinctApplications(): Promise<string[]> { async getDistinctApplications(): Promise<string[]> {
const rows = await this.db const rows = await this.db

View File

@ -275,6 +275,17 @@ export default class ClientInstanceService {
return sdkApps.filter((sdkApp) => isOutdatedSdk(sdkApp.sdkVersion)); return sdkApps.filter((sdkApp) => isOutdatedSdk(sdkApp.sdkVersion));
} }
async getOutdatedSdksByProject(
projectId: string,
): Promise<OutdatedSdksSchema['sdks']> {
const sdkApps =
await this.clientInstanceStore.groupApplicationsBySdkAndProject(
projectId,
);
return sdkApps.filter((sdkApp) => isOutdatedSdk(sdkApp.sdkVersion));
}
async usesSdkOlderThan( async usesSdkOlderThan(
sdkName: string, sdkName: string,
sdkVersion: string, sdkVersion: string,

View File

@ -18,6 +18,8 @@ import {
createResponseSchema, createResponseSchema,
type DeprecatedProjectOverviewSchema, type DeprecatedProjectOverviewSchema,
deprecatedProjectOverviewSchema, deprecatedProjectOverviewSchema,
outdatedSdksSchema,
type OutdatedSdksSchema,
type ProjectDoraMetricsSchema, type ProjectDoraMetricsSchema,
projectDoraMetricsSchema, projectDoraMetricsSchema,
projectOverviewSchema, projectOverviewSchema,
@ -41,17 +43,21 @@ import { projectApplicationsQueryParameters } from '../../openapi/spec/project-a
import { normalizeQueryParams } from '../feature-search/search-utils'; import { normalizeQueryParams } from '../feature-search/search-utils';
import ProjectInsightsController from '../project-insights/project-insights-controller'; import ProjectInsightsController from '../project-insights/project-insights-controller';
import FeatureLifecycleController from '../feature-lifecycle/feature-lifecycle-controller'; import FeatureLifecycleController from '../feature-lifecycle/feature-lifecycle-controller';
import type ClientInstanceService from '../metrics/instance/instance-service';
export default class ProjectController extends Controller { export default class ProjectController extends Controller {
private projectService: ProjectService; private projectService: ProjectService;
private openApiService: OpenApiService; private openApiService: OpenApiService;
private clientInstanceService: ClientInstanceService;
private flagResolver: IFlagResolver; private flagResolver: IFlagResolver;
constructor(config: IUnleashConfig, services: IUnleashServices, db: Db) { constructor(config: IUnleashConfig, services: IUnleashServices, db: Db) {
super(config); super(config);
this.projectService = services.projectService; this.projectService = services.projectService;
this.clientInstanceService = services.clientInstanceService;
this.openApiService = services.openApiService; this.openApiService = services.openApiService;
this.flagResolver = config.flagResolver; this.flagResolver = config.flagResolver;
@ -160,6 +166,26 @@ export default class ProjectController extends Controller {
], ],
}); });
this.route({
method: 'get',
path: '/:projectId/sdks/outdated',
handler: this.getOutdatedProjectSdks,
permission: NONE,
middleware: [
this.openApiService.validPath({
tags: ['Unstable'],
operationId: 'getOutdatedProjectSdks',
summary: 'Get outdated project SDKs',
description:
'Returns a list of the outdated SDKS with the applications using them.',
responses: {
200: createResponseSchema('outdatedSdksSchema'),
...getStandardResponses(404),
},
}),
],
});
this.use( this.use(
'/', '/',
new ProjectFeaturesController( new ProjectFeaturesController(
@ -310,4 +336,21 @@ export default class ProjectController extends Controller {
serializeDates(applications), serializeDates(applications),
); );
} }
async getOutdatedProjectSdks(
req: IAuthRequest<IProjectParam>,
res: Response<OutdatedSdksSchema>,
) {
const { projectId } = req.params;
const outdatedSdks =
await this.clientInstanceService.getOutdatedSdksByProject(
projectId,
);
this.openApiService.respondWithValidation(
200,
res,
outdatedSdksSchema.$id,
{ sdks: outdatedSdks },
);
}
} }

View File

@ -29,6 +29,9 @@ export interface IClientInstanceStore
groupApplicationsBySdk(): Promise< groupApplicationsBySdk(): Promise<
{ sdkVersion: string; applications: string[] }[] { sdkVersion: string; applications: string[] }[]
>; >;
groupApplicationsBySdkAndProject(
projectId: string,
): Promise<{ sdkVersion: string; applications: string[] }[]>;
getDistinctApplications(): Promise<string[]>; getDistinctApplications(): Promise<string[]>;
getDistinctApplicationsCount(daysBefore?: number): Promise<number>; getDistinctApplicationsCount(daysBefore?: number): Promise<number>;
deleteForApplication(appName: string): Promise<void>; deleteForApplication(appName: string): Promise<void>;

View File

@ -158,7 +158,7 @@ test('should show correct application metrics', async () => {
]); ]);
const { body: outdatedSdks } = await app.request const { body: outdatedSdks } = await app.request
.get(`/api/admin/metrics/sdks/outdated`) .get(`/api/admin/projects/default/sdks/outdated`)
.expect(200); .expect(200);
expect(outdatedSdks).toMatchObject({ expect(outdatedSdks).toMatchObject({

View File

@ -49,6 +49,12 @@ export default class FakeClientInstanceStore implements IClientInstanceStore {
); );
} }
async groupApplicationsBySdkAndProject(
projectId: string,
): Promise<{ sdkVersion: string; applications: string[] }[]> {
throw new Error('Not implemented in mock');
}
async deleteAll(): Promise<void> { async deleteAll(): Promise<void> {
this.instances = []; this.instances = [];
} }