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:
parent
1b1bde8aec
commit
4972b9686c
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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([]);
|
||||
});
|
@ -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';
|
||||
|
@ -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,
|
||||
|
@ -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: {
|
||||
|
Loading…
Reference in New Issue
Block a user