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

feat: project applications controller/service layer (#6184)

Just adding controller/service layer, connecting with schema.
Next PR will implement store and e2e tests.
This commit is contained in:
Jaanus Sellin 2024-02-09 13:18:26 +02:00 committed by GitHub
parent 1b1bde8aec
commit 4972b9686c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 122 additions and 34 deletions

View File

@ -1,5 +1,5 @@
import { Response } from 'express';
import Controller from '../../controller';
import Controller from '../../routes/controller';
import {
IArchivedQuery,
IProjectParam,
@ -7,12 +7,12 @@ import {
IUnleashServices,
NONE,
serializeDates,
} from '../../../types';
import ProjectFeaturesController from '../../../features/feature-toggle/feature-toggle-controller';
import EnvironmentsController from '../../../features/project-environments/environments';
import ProjectHealthReport from './health-report';
import ProjectService from '../../../services/project-service';
import VariantsController from './variants';
} from '../../types';
import ProjectFeaturesController from '../feature-toggle/feature-toggle-controller';
import EnvironmentsController from '../project-environments/environments';
import ProjectHealthReport from '../../routes/admin-api/project/health-report';
import ProjectService from '../../services/project-service';
import VariantsController from '../../routes/admin-api/project/variants';
import {
createResponseSchema,
ProjectDoraMetricsSchema,
@ -22,18 +22,22 @@ import {
projectsSchema,
ProjectsSchema,
projectOverviewSchema,
} from '../../../openapi';
import { getStandardResponses } from '../../../openapi/util/standard-responses';
import { OpenApiService, SettingService } from '../../../services';
import { IAuthRequest } from '../../unleash-types';
import { ProjectApiTokenController } from './api-token';
import ProjectArchiveController from './project-archive';
import { createKnexTransactionStarter } from '../../../db/transaction';
import { Db } from '../../../db/db';
import DependentFeaturesController from '../../../features/dependent-features/dependent-features-controller';
import { ProjectOverviewSchema } from '../../../openapi/spec/project-overview-schema';
} from '../../openapi';
import { getStandardResponses } from '../../openapi/util/standard-responses';
import { OpenApiService, SettingService } from '../../services';
import { IAuthRequest } from '../../routes/unleash-types';
import { ProjectApiTokenController } from '../../routes/admin-api/project/api-token';
import ProjectArchiveController from '../../routes/admin-api/project/project-archive';
import { createKnexTransactionStarter } from '../../db/transaction';
import { Db } from '../../db/db';
import DependentFeaturesController from '../dependent-features/dependent-features-controller';
import { ProjectOverviewSchema } from '../../openapi/spec/project-overview-schema';
import {
projectApplicationsSchema,
ProjectApplicationsSchema,
} from '../../openapi/spec/project-applications-schema';
export default class ProjectApi extends Controller {
export default class ProjectController extends Controller {
private projectService: ProjectService;
private settingService: SettingService;
@ -129,6 +133,26 @@ export default class ProjectApi extends Controller {
],
});
this.route({
method: 'get',
path: '/:projectId/applications',
handler: this.getProjectApplications,
permission: NONE,
middleware: [
services.openApiService.validPath({
tags: ['Unstable'],
operationId: 'getProjectApplications',
summary: 'Get a list of all applications for a project.',
description:
'This endpoint returns an list of all the applications for a project.',
responses: {
200: createResponseSchema('projectApplicationsSchema'),
...getStandardResponses(401, 403, 404),
},
}),
],
});
this.use(
'/',
new ProjectFeaturesController(
@ -229,4 +253,21 @@ export default class ProjectApi extends Controller {
dora,
);
}
async getProjectApplications(
req: IAuthRequest,
res: Response<ProjectApplicationsSchema>,
): Promise<void> {
const { projectId } = req.params;
const applications =
await this.projectService.getApplications(projectId);
this.openApiService.respondWithValidation(
200,
res,
projectApplicationsSchema.$id,
applications,
);
}
}

View File

@ -1,14 +1,14 @@
import dbInit, { ITestDb } from '../../../helpers/database-init';
import dbInit, { ITestDb } from '../../../test/e2e/helpers/database-init';
import {
IUnleashTest,
insertFeatureEnvironmentsLastSeen,
insertLastSeenAt,
setupAppWithCustomConfig,
} from '../../../helpers/test-helper';
import getLogger from '../../../../fixtures/no-logger';
} from '../../../test/e2e/helpers/test-helper';
import getLogger from '../../../test/fixtures/no-logger';
import { IProjectStore } from '../../../../../lib/types';
import { DEFAULT_ENV } from '../../../../../lib/util';
import { IProjectStore } from '../../types';
import { DEFAULT_ENV } from '../../util';
let app: IUnleashTest;
let db: ITestDb;
@ -143,9 +143,11 @@ test('response for default project should include created_at', async () => {
.expect(200);
expect(body.createdAt).toBeDefined();
});
test('response for project overview should include feature type counts', async () => {
await app.createFeature({ name: 'my-new-release-toggle', type: 'release' });
await app.createFeature({
name: 'my-new-release-toggle',
type: 'release',
});
await app.createFeature({
name: 'my-new-development-toggle',
type: 'development',
@ -156,8 +158,14 @@ test('response for project overview should include feature type counts', async (
.expect(200);
expect(body).toMatchObject({
featureTypeCounts: [
{ type: 'development', count: 1 },
{ type: 'release', count: 1 },
{
type: 'development',
count: 1,
},
{
type: 'release',
count: 1,
},
],
});
});
@ -278,3 +286,12 @@ 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('should return empty list of applications', async () => {
const { body } = await app.request
.get('/api/admin/projects/default/applications')
.expect('Content-Type', /json/)
.expect(200);
expect(body).toMatchObject([]);
});

View File

@ -175,3 +175,5 @@ export * from './feature-type-count-schema';
export * from './feature-search-response-schema';
export * from './inactive-user-schema';
export * from './inactive-users-schema';
export * from './project-application-schema';
export * from './project-applications-schema';

View File

@ -20,7 +20,7 @@ import UserAdminController from './user-admin';
import EmailController from './email';
import UserFeedbackController from './user-feedback';
import UserSplashController from './user-splash';
import ProjectApi from './project/project-api';
import ProjectController from '../../features/project/project-controller';
import { EnvironmentsController } from './environments';
import ConstraintsController from './constraints';
import PatController from './user/pat';
@ -117,7 +117,10 @@ class AdminApi extends Controller {
'/feedback',
new UserFeedbackController(config, services).router,
);
this.app.use('/projects', new ProjectApi(config, services, db).router);
this.app.use(
'/projects',
new ProjectController(config, services, db).router,
);
this.app.use(
'/environments',
new EnvironmentsController(config, services).router,

View File

@ -63,7 +63,10 @@ import { calculateAverageTimeToProd } from '../features/feature-toggle/time-to-p
import { IProjectStatsStore } from '../types/stores/project-stats-store-type';
import { uniqueByKey } from '../util/unique';
import { BadDataError, PermissionError } from '../error';
import { ProjectDoraMetricsSchema } from '../openapi';
import {
ProjectDoraMetricsSchema,
ProjectApplicationsSchema,
} from '../openapi';
import { checkFeatureNamingData } from '../features/feature-naming-pattern/feature-naming-validation';
import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType';
import EventService from '../features/events/event-service';
@ -89,7 +92,14 @@ interface ICalculateStatus {
updates: IProjectStats;
}
function includes(list: number[], { id }: { id: number }): boolean {
function includes(
list: number[],
{
id,
}: {
id: number;
},
): boolean {
return list.some((l) => l === id);
}
@ -887,7 +897,16 @@ export default class ProjectService {
featureToggleNames,
);
return { features: toggleAverage, projectAverage: projectAverage };
return {
features: toggleAverage,
projectAverage: projectAverage,
};
}
async getApplications(
projectId: string,
): Promise<ProjectApplicationsSchema> {
return [];
}
async changeRole(
@ -1091,7 +1110,10 @@ export default class ProjectService {
const [projectActivityCurrentWindow, projectActivityPastWindow] =
await Promise.all([
this.eventStore.queryCount([
{ op: 'where', parameters: { project: projectId } },
{
op: 'where',
parameters: { project: projectId },
},
{
op: 'beforeDate',
parameters: {
@ -1101,7 +1123,10 @@ export default class ProjectService {
},
]),
this.eventStore.queryCount([
{ op: 'where', parameters: { project: projectId } },
{
op: 'where',
parameters: { project: projectId },
},
{
op: 'betweenDate',
parameters: {